English Русский 中文 Español Deutsch Português
preview
チャート上で取引を視覚化する(第1回):分析期間の選択

チャート上で取引を視覚化する(第1回):分析期間の選択

MetaTrader 5トレーディングシステム | 7 11月 2024, 10:37
114 0
Aleksandr Seredin
Aleksandr Seredin

はじめに

この記事では、取引決定の遡及分析中に取引を視覚化するためのスクリプトをゼロから開発します。簡単に言えば、手動で取引し、過去の市場参入を分析して取引パフォーマンスを向上させる場合、手動の履歴分析とそれに関連する純粋に技術的な作業にできるだけ時間を費やさないようにする必要があります。具体的には、チャートを開く、履歴内の取引を検索する、端末で手動で印刷画面を保存する、完了した取引のストップロスとテイクプロフィットを個別に描画する、支払われた手数料やスワップに関する情報を収集するなどです。ここで開発するスクリプトは、すべての機械的な作業を大幅に削減するのに役立ちます。スクリプト(以下の添付ファイルから収集)を使用することで、技術的な作業に費やす時間を大幅に削減し、取引決定の分析にもっと注意を払うことができます。プロジェクトの組み立てに時間を費やしたくない方は、MQL5マーケットでスクリプトの既製バージョンをダウンロードできます。スクリプトの開発は2つの部分に分かれており、各部分にソースコードが完全に添付されています。


遡及分析が必要な理由

市場に参入するすべての人の主な目標は、リスクをコントロールしながら長期的な利益を上げることです。このビジネスは、リスクに対処しなければならない他のビジネスと比較することができます。しかし、実例が示すように、実体経済におけるほとんどの新規ビジネスは最終的に倒産してしまいます。金融市場では、レバレッジの使用と利用可能なすべての資金の損失が完全に帰結するため、資金がより早く失われる可能性があります。実際のビジネスでは、何か問題が発生した場合、生産設備を他の目的に向け直す(抵当に入れていない場合)ことができ、再スタートを切ることができます。しかし、取引では、口座全体がすべての損失を負担します。

複数の統計が、リスク管理をせずにレバレッジを不当に利用して金融市場に投資することは、資本投資リスクの観点から最も危険な投資になり得ることを示しています。 米国証券取引委員会が17年にわたっておこなった調査(リリース番号34-64874、ファイル番号S7-30-11)によると、

「顧客の約70%が四半期ごとに損失を被っており、小売顧客の投資の平均100%が12か月以内に失われています。」

リスクバランスリスク管理の重要性に関する本連載の前回の記事では、制御されていないリスクは常に損失につながり、リスク管理がなければ最初は利益があった戦略でさえ不採算に変わる可能性があることを指摘しました。本連載では、市場が非常に柔軟な存在であり、さまざまな経済的および政治的要因の影響を受けて時間とともに変化するという側面を検討します。より明確な理解のために、市場には少なくとも2つの段階、つまり横這いの動きとトレンドがあると言えます。したがって、選択した戦略が市場の状況に適しているかどうかを判断するために、取引活動を常に分析することが非常に重要です。

多くのアルゴリズムトレーダーは、特定の戦略が横這い市場ではうまく機能するが、市場が動くと損失が出始めることに気づいています。同様に、トレンドに適応した戦略は横這い市場では効率が悪くなります。損失が利益を食いつぶし始める前に、市場のフェーズの変化を認識できるアルゴリズムを作成するには、かなりの計算リソースと時間が必要です。

これらの問題により、トレーダーは常に「すべてを正しくおこなっているのか」、「今日のドローダウンは正常か、それともアルゴリズムを調整する必要があるか」、「結果を改善するにはどうすればよいか」と自問する必要があります。これらの質問への答えは、市場での長期的な成功にとって重要です。答えを見つける方法はさまざまです。戦略オプティマイザーを使用するもの、ディープニューラルネットワークを適用するもの、数学モデルや長年の経験に頼るものがあります。市場自体が主な教師であるため、これらすべてのアプローチが効果的です。

市場を監視することは、革新的な戦略や投資アイデアの源になることがよくあります。この記事では、取引の効率を向上させるだけでなく、データ分析に基づいてアルゴリズムの新しいアイデアを提供するスクリプトを作成します。私は長い間EAのみを使用して取引をおこなってきましたが、過去の取引の分析は私にとって必須です。

次に、取引を分析する日常的なプロセスを大幅に簡素化するスクリプトの作成に移りましょう。スクリプトは最終的に、図1に示すように、印刷画面に情報を出力します。

図1:スクリプトデータの表示

図1:スクリプトデータの表示

ユーザーの入力データを入力してスクリプトの実装を開始します。


スクリプト入力

スクリプトの機能には、過去の取引に関するデータをチャート印刷画面に自動的にダウンロードする機能、各取引の複数の時間枠を別のファイルに設定する機能、およびユーザーが指定した期間の個別の取引またはすべての取引のデータをダウンロードするオプションを提供する機能が含まれる必要があります。また、結果の印刷画面のグラフィックスをカスタマイズする機会を最大限に提供するよう努めます。

ユーザーがスクリプトを切り替えて、1つの取引または履歴データの期間のデータをアンロードできるようにするためには、まず、次の形式でenum列挙型のカスタムデータ型を提供する必要があります。

enum input_method
  {
   Select_one_deal,
   Select_period
  };

カスタム列挙の実装は、以前に発表された2つのオプション(1つの取引の選択と期間の選択)のみで構成されます。これで、inputメモリクラスのグローバルレベルで入力を宣言する手順に進むことができます。

input group "Enter deal ticket or select the period"
input input_method inp_method = Select_period;                          // One deal or period?

ユーザーの利便性のために、ここではgroupキーワードを使用して名前付きブロックを提供し、ユーザーにとって重要な各パラメータを視覚的に区別し、必要な説明をおこないます。また、inputメモリクラスの変数をコメントアウトし、変数名を一般的なユーザーが理解しやすいテキストコメントに置き換えます。

グラフィカルインターフェイスで変数値を入力すると、図2に示すようになります。

図2:データアンロード条件を入力するためのユーザーインターフェイス

図2:データのアンロード条件を入力するためのユーザーインターフェイス

ユーザーが特定の取引に関する情報を迅速に取得できるように、inp_method変数のSelect_one_dealに対応する列挙を設定する必要があります。その後、ユーザーは必要な取引の番号(チケット)を指定する必要があります。次の形式で入力ブロックを宣言します。

input group "For case 'Select_one_deal': Enter ticket of deal"
input long inp_d_ticket = 4339491;                                      // Ticket (global id)

変数のデフォルト値を設定し、ユーザーへの例として使用します。また、これはグローバル番号による注文チケットであることを明示します。通常、端末は取引履歴にこの番号を表示するため、ユーザーがこの点で悩むことはありません。

ただし、ユーザーがすべての取引がダウンロードされるように分析期間を選択する場合、履歴内の取引を選択するための開始日と終了日の入力を提供する必要があります。これは、グローバルレベルで次のエントリを通じて実装できます。

input group "For case 'Select_period': Enter period for analys"
input datetime start_date = D'17.07.2023';                              // Start date
input datetime finish_date = D'19.07.2023';                             // Finish date

ユーザーは、サンプルの開始日をstart_date変数に入力し、サンプル期間の終了日をfinish_date変数に入力します。ユーザーの利便性を考慮し、これらの変数もデフォルト値で初期化しましょう。

分析する取引についてある程度明確になったので、次にユーザーが1つの取引のデータを複数のチャートに保存できるメカニズムを実装します。たとえば、手動取引中にトレーダーが日足チャートを使用して取引レベルを決定し、M30チャートでエントリーポイントを探す場合、これは非常に便利です。スクリプトがすべてのデータを含むチャート(M30とD1の両方)をすぐにダウンロードすれば、過去の取引を分析するのがはるかに簡単になります。

また、このアイデアを入力を通じて実装し、ユーザーの機能を拡張するために、取引に2つではなく4つのチャートを提供します。トレーダーの中には、取引で2つ以上のチャートを使用する人もいますが、4つ以上を使用する人はほとんどいません。例外的なケースでは、スクリプトを複数回実行することができます。これをおこなうには、ENUM_TIMEFRAMES列挙標準型の4つの変数を宣言します。main_graph変数はメインのアンロード時間枠を示し、残りは補助的なものです。次の形式でコードを記述してみましょう。

input group "Enter time frames. 'current' = don't use"
input ENUM_TIMEFRAMES main_graph = PERIOD_D1;                           // Period of main chart
input ENUM_TIMEFRAMES addition_graph = PERIOD_H1;                       // Period of addition chart
input ENUM_TIMEFRAMES addition_graph_2 = PERIOD_CURRENT;                // Period of addition chart #2
input ENUM_TIMEFRAMES addition_graph_3 = PERIOD_CURRENT;                // Period of addition chart #3

ユーザーが選択した期間は時間によって大きく異なる可能性があります。実際、これは上記の設定で提供されていたものです。そのため、右側のチャートシフトも必要になります。これにより、ユーザーが使いやすいように画像がチャート上にきちんと配置されます。つまり、日足チャートの印刷画面を作成すると、取引後の数本のバーで、取引後の価格がどこにおこなったかを確認するのに十分です。より短い時間枠の場合、はるかに高い値を持つ対応する設定が必要になります。したがって、以下のコードのように、チャート上のこれらのシフトをデフォルト値で提供します。

input group "Navigate settings in bars."
input int bars_from_right_main = 15;                                    // Shift from right on main chart
input int bars_from_right_add = 35;                                     // Shift from right on addition chart
input int bars_from_right_add_2 = 35;                                   // Shift from right on addition chart #2
input int bars_from_right_add_3 = 35;                                   // Shift from right on addition chart #3

MetaTrader 5端末で印刷画面を保存するためにチャートをカスタマイズするには、テンプレート機能を適用するか、チャート操作コードを実装してすべてのチャートプロパティを構成し、各値を個別のスクリプト入力に割り当てることができます。私のソリューションの場合、チャート設定を使用する際にスクリプト入力でセクションを「膨らませる」のではなく、端末でチャートの表示を操作するための既製のテンプレートを使用する方がエレガントで正しいと思います。その結果、各時間枠の入力に事前に準備されたテンプレートの名前を書き込むだけで、スクリプトがそれを使用して動作します。以前に作成したテンプレートを利用して、表示をより使いやすくすることもできます。

MetaTrader 5端末のチャートの表示は、ほぼすべての好みに合わせてカスタマイズできます。チャート上でF8キーを押すと、表示モード、オブジェクト、カラーパレットなどをカスタマイズできます。さまざまな表示構成のテンプレートを作成することで、チャートの設定をすばやく簡単に変更できます。コンテキストメニュー項目の[チャート] -> [テンプレート] -> [テンプレートの保存/テンプレートの読み込み]を使用すると、アクティブなチャートウィンドウを変更せずに、価格チャートの表示設定をすばやく変更できます。その結果、分析された時間枠の数に応じて、複数のチャート表示設定が複数の変数に適合します。

input group "Properties of charts. Enter the template name:"
input string main_template = "dailyHistorytemp";                        // Template name of main chart
input string addition_template = "hourHistoryTemp";                     // Template name of addition chart
input string addition_template_2 = "hourHistoryTemp";                   // Template name of addition chart #2
input string addition_template_3 = "hourHistoryTemp";                   // Template name of addition chart #3

端末入力のユーザーインターフェイスでは、図3に示すようになります。

図3:テンプレート入力のユーザーインターフェイス

図3:テンプレート入力のユーザーインターフェイス

標準設定をすべて決定したので、取引操作を分析するための情報をユーザーに完全に提供するために、取引の完全な情報を表示することに特に関連する設定を追加しましょう。これらは主に、ポジションの始値、ストップロス、テイクプロフィット、および接続線に対応するオブジェクトです。対応するカラー型変数は次のようになります。

input group "Colors of deals line"
input color clr_price_open = clrWhiteSmoke;                             // Color of price open label
input color clr_price_close = clrWhiteSmoke;                            // Color of price close label
input color clr_stop = clrRed;                                          // Color of stop loss label
input color clr_take = clrLawnGreen;                                    // Color of take profit label
input color clr_main = clrWhiteSmoke;                                   // Color of deals trendline

一般に、スクリプトのすべての入力は以下のように記述されます。

#property copyright "Visit product page"
#property link      "https://www.mql5.com/ru/market/product/86223"
#property version   "1.00"
#property description "Make an automatic printscreen with a full description of all transactions for the period or 
			specify the ticket of the desired transaction."
#property script_show_inputs

enum input_method
  {
   Select_one_deal,
   Select_period
  };


input group "Enter deal ticket or select the period"
input input_method inp_method = Select_period;                          // One deal or period?

input group "For case 'Select_one_deal': Enter ticket of deal"
input long inp_d_ticket = 4339491;                                      // Ticket (global id)

input group "For case 'Select_period': Enter period for analys"
input datetime start_date = D'17.07.2023';                              // Start date
input datetime finish_date = D'19.07.2023';                             // Finish date

input group "Enter time frames. 'current' = don't use"
input ENUM_TIMEFRAMES main_graph = PERIOD_D1;                           // Period of main chart
input ENUM_TIMEFRAMES addition_graph = PERIOD_H1;                       // Period of addition chart
input ENUM_TIMEFRAMES addition_graph_2 = PERIOD_CURRENT;                // Period of addition chart #2
input ENUM_TIMEFRAMES addition_graph_3 = PERIOD_CURRENT;                // Period of addition chart #3

input group "Navigate settings in bars."
input int bars_from_right_main = 15;                                    // Shift from right on main chart
input int bars_from_right_add = 35;                                     // Shift from right on addition chart
input int bars_from_right_add_2 = 35;                                   // Shift from right on addition chart #2
input int bars_from_right_add_3 = 35;                                   // Shift from right on addition chart #3

input group "Properties of charts. Enter the template name:"
input string main_template = "dailyHistorytemp";                        // Template name of main chart
input string addition_template = "hourHistoryTemp";                     // Template name of addition chart
input string addition_template_2 = "hourHistoryTemp";                   // Template name of addition chart #2
input string addition_template_3 = "hourHistoryTemp";                   // Template name of addition chart #3

input group "Colors of deals line"
input color clr_price_open = clrWhiteSmoke;                             // Color of price open label
input color clr_price_close = clrWhiteSmoke;                            // Color of price close label
input color clr_stop = clrRed;                                          // Color of stop loss label
input color clr_take = clrLawnGreen;                                    // Color of take profit label
input color clr_main = clrWhiteSmoke;                                   // Color of deals trendline

グローバルレベルですべての変数を定義したので、OnStart()のスクリプトエントリポイントでコードの実装に進むことができます。まず、保存された印刷画面ファイルに送信されるデータを保存、処理、表示するために必要なすべての変数を定義します。スクリプトを処理するときに、各ステップをユーザーに通知します。

まず、スクリプトが開始されたことをユーザーに通知し、何か問題が発生した場合にエラー戻りコードを正しく確認できるようにエラー変数をリセットし、すべてのポジションプロパティの変数を提供し、すべての取引に関する情報を収集するための適切なストレージを提供しましょう。

   Print("Script starts its work.");                                    // notified

   ResetLastError();                                                    // reset error

   string brok_name = TerminalInfoString(TERMINAL_COMPANY);             // get broker name
   long account_num = AccountInfoInteger(ACCOUNT_LOGIN);                // get account number

//---
   ulong    ticket = 0;                                                 // ticket
   ENUM_DEAL_ENTRY entry = -1;                                          // entry or exit
   long     position_id = 0,  PositionID[];                             // main id
   int      type = -1,        arr_type[];                               // deal type
   int      magic = -1,       arr_magic[];                              // magic number
   ENUM_DEAL_REASON      reason = -1,      arr_reason[];                // reason

   datetime time_open = 0,    arr_time_open[];                          // deal open time
   datetime time_close = 0,   arr_time_close[];                         // close time

   string   symbol,           arr_symbol[];                             // symbol
   string   comment,          arr_comment[];                            // comment
   string   externalID,       arr_extermalID[];                         // external id

   double   stop_loss = 0,    arr_stop_loss[];                          // deal Stop Loss
   double   take_profit = 0,  arr_take_profit[];                        // deal Take Profit
   double   open = 0,         arr_open[];                               // open price
   double   close = 0,        arr_close[];                              // close price
   double   volume = 0,       arr_volume[];                             // position volume
   double   commission = 0,   arr_commission[];                         // commission
   double   swap = 0,         arr_swap[];                               // swap
   double   profit = 0,       arr_profit[];                             // profit
   double   fee = 0,          arr_fee[];                                // fee

   int res = -1;                                                        // user command

これで、ユーザーが1つの取引の印刷画面を受け取るか、特定の期間内のすべての取引の印刷画面を受け取るかに応じて、取引データの収集を実装できるようになりました。


期間別に履歴データを選択する

入力されたinp_methodグローバル変数の値を取得し、特定の期間内に完了した取引に関するデータを収集するためのSelect_periodケースバリアントから処理を開始するswitch論理選択演算子を使用してユーザー選択を実装しましょう。

まず、期間内の取引を分析するオプションが入力で選択されたことをユーザーに通知します。通知は、メッセージウィンドウを呼び出すための定義済関数MessageBox()を使用して実装されます。3番目のパラメータは、端末が[キャンセル]をクリックした後にスクリプトの実行を中断できるようにするMB_OKCANCEL定数です。これは、ユーザーがinp_method入力に誤って間違ったオプションを入力した場合、スクリプトを途中で終了して実行を待たずに済むため便利です。完全なコードを以下に示します。

         res = MessageBox("You have selected analysis for period. Continue?","",MB_OKCANCEL); // wait for user confirmation 

スクリプトを中断するメカニズムを実装するために、ボタン押下イベントの処理結果をres変数に格納します。厳密には、最も簡単に中断する方法はreturn文を使用することです。変数resにIDCANCEL値が含まれている場合、つまりユーザーが対応するボタンを押した場合、returnが返されます。このブロックは、次の形式でif条件付き論理選択演算子によって表されます。

         if(res == IDCANCEL)                                            // if interrupted by user
           {
            printf("%s - %d -> Scrypt was stoped by user.",__FUNCTION__,__LINE__); // notify
            return;                                                     // do not continue
           }

この段階でユーザーがオプション選択の有効性を確認した場合、指定された履歴期間の完了した取引に関する情報の収集を開始します。定義済み関数HistorySelect()を使用して、過去の取引の選択を実行します。上記のユーザーが入力して確認した期間の開始値と終了値を受け取るのはこの関数です。

過去の取引をリクエストした後、コードの最適化とユーザーの利便性のために、ユーザーが指定した期間内に口座に取引が存在するかどうかを確認するように手配することが非常に適切です。定義済みのHistoryDealsTotal()端末関数を使用して、取得した過去の取引数をtotal変数に格納します。

            int total = HistoryDealsTotal();                            // got the total number of deals

指定された期間内に分析するものがなく、取引が1件も見つからなかった場合、ユーザーにその旨を通知し、スクリプトを停止します。このイベントは、条件付き論理選択演算子ifによっても処理され、EAログと情報ウィンドウを通じて、指定された期間内に取引がなかったことをユーザーに知らせます。以下に示すように、return演算子を使用してスクリプトを中断します。以下に示すように、return演算子を使用してスクリプトを中断します。

            if(total <= 0)                                              // if nothing found
              {
               printf("%s - %d -> No deals were found for the specified period.",__FUNCTION__,__LINE__); // notify
               MessageBox("No deals were found for the specified period: "+TimeToString(start_date)+"-"+TimeToString(finish_date)+". Script is done.");
               return;
              }

期間内に取引が見つかった場合は、データの収集を開始できます。以下に示すように、forループを使用して、履歴で取得されたすべての取引を反復処理します。

            for(int i=0; i<total; i++)                                  // iterate through the number of deals

各履歴取引のデータを選択して要求するには、固有のID(チケット)を使用します。これを実現するために、定義済みの関数HistoryDealGetTicket()を利用します。この関数は、0からtotalまでのシリアル番号をパラメータとして受け取り、各取引の固有の取引IDを返します。返された値の有効性を確認することが重要です。

               //--- try to get deals ticket
               if((ticket=HistoryDealGetTicket(i))>0)                   // took the ticket

過去の取引チケットを受け取った後、取引から一般的なポジションデータを収集するために必要な3つの主な機能を要求します。これらの機能には、過去の取引が属するポジションのID、取引が何を初期化したのか(ポジションの開始または終了、およびDEAL_TYPE機能による注文の方向とタイプを定義する取引タイプ)を通知するENUM_DEAL_ENTRY機能が含まれます。これら3つの要求はすべて、以下に示すように、HistoryDealGetInteger()という定義済みの端末関数を通じて実行されます。

                  //--- get deals properties
                  position_id = HistoryDealGetInteger(ticket,DEAL_POSITION_ID);        // took the main id
                  entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket,DEAL_ENTRY);   // entry or exit?
                  type = (int)HistoryDealGetInteger(ticket,DEAL_TYPE);  	       // deal type

このデータの主な要求は、その値によってポジション全体のデータを収集するためのデータを取得する際に使用される値が決まるという点にあります。一般的なポジションのデータは、このポジションに関連する複数の注文のデータセットから集計され、ポジションIDに基づいて相互に比較されることを思い出してください。

まず、残高の補充や資金の引き出し、ブローカーからのボーナスの発生など、取引操作に直接関係のないすべての履歴取引を整理する必要があります。これを実現するために、コード内に以下のチェックを配置します。

                  //--- check the deal type
                  if(type == DEAL_TYPE_BUY            ||                // if it is buy
                     type == DEAL_TYPE_SELL           ||                // if it is sell
                     type == DEAL_TYPE_BUY_CANCELED   ||                // if canceled buy
                     type == DEAL_TYPE_SELL_CANCELED                    // if canceled sell
                    )

受け取った取引が取引操作、つまり買いまたは売り、あるいはそれに対応するクローズ(ブローカーアカウントや市場の種類によって異なる場合があります)であることを確認した後、ポジション全体のデータを収集し始めることができます。ポジションのデータを対応する注文に保存する特性を考慮して、ポジションの開始に関連する注文からいくつかのポジション機能を取得し、ポジションのクローズに関連する注文からもいくつかの機能を取得します。たとえば、ポジションの財務結果に関するデータは、ポジションのクローズ注文で論理的に予想どおりに見つけることができます。このデータをより視覚的に明確に表示するために、次の表に収集します。

# ポジションの開始(DEAL_ENTRY_IN) ポジションの終了(DEAL_ENTRY_OUT)
 1  open (DEAL_PRICE)  close (DEAL_PRICE)
 2  time_open (DEAL_TIME)  time_close (DEAL_TIME)
 3  symbol (DEAL_SYMBOL)  reason (DEAL_REASON)
 4  stop_loss (DEAL_SL)  swap (DEAL_SWAP)
 5  take_profit (DEAL_TP)  profit (DEAL_PROFIT)
 6  magic (DEAL_MAGIC)  fee (DEAL_FEE)
 7  comment (DEAL_COMMENT)  -
 8  externalID (DEAL_EXTERNAL_ID)  -
 9  volume (DEAL_VOLUME)  -
 10   commission (DEAL_COMMISSION)  -

表1:取引タイプに応じたポジション全体のデータの取得元

コードでは、表1に示すデータの要求と並べ替えは次のようになります。

                     if(entry == DEAL_ENTRY_IN)                         		// if this is an entry
                       {
                        open = HistoryDealGetDouble(ticket,DEAL_PRICE);                 // take open price
                        time_open  =(datetime)HistoryDealGetInteger(ticket,DEAL_TIME);  // take open time
                        symbol=HistoryDealGetString(ticket,DEAL_SYMBOL);   		// take symbol
                        stop_loss = HistoryDealGetDouble(ticket,DEAL_SL);  		// take Stop Loss
                        take_profit = HistoryDealGetDouble(ticket,DEAL_TP);		// take Take Profit

                        magic = (int)HistoryDealGetInteger(ticket,DEAL_MAGIC);   	// take magic number
                        comment=HistoryDealGetString(ticket,DEAL_COMMENT);       	// take comment
                        externalID=HistoryDealGetString(ticket,DEAL_EXTERNAL_ID);	// take external id
                        volume = HistoryDealGetDouble(ticket,DEAL_VOLUME);          	// take volume
                        commission = HistoryDealGetDouble(ticket,DEAL_COMMISSION);  	// take commission value
                       }

                     if(entry == DEAL_ENTRY_OUT)                        	 	// if this is an exit
                       {
                        close = HistoryDealGetDouble(ticket,DEAL_PRICE);               	// take close price
                        time_close  =(datetime)HistoryDealGetInteger(ticket,DEAL_TIME);	// take close time

                        reason = (ENUM_DEAL_REASON)HistoryDealGetInteger(ticket,DEAL_REASON); // 
                        swap = HistoryDealGetDouble(ticket,DEAL_SWAP);        		// swap
                        profit = HistoryDealGetDouble(ticket,DEAL_PROFIT);    		// profit
                        fee = HistoryDealGetDouble(ticket,DEAL_FEE);          		// fee
                       }

ポジションデータが事前に取得された後、関連情報を格納するためのコンテナを編成する必要があります。この機能を実装する際には、各機能に標準の1次元配列を使用します。ストレージ内にポジションが存在するかどうかを確認するために、小さなFind()テンプレート関数を定義します。この関数は、コンテナ内に特定のポジションが存在するかどうかを確認するために使用されます。ロジックとしては、コンテナとその中で検索する値を関数のパラメータとして渡す形になります。具体的には、取引が属するポジションのIDを検索します。ポジションが見つかった場合、関数は対応するインデックスを返し、見つからない場合は-1を返します。

また、単一のポジションの各プロパティを文字列、整数値、小数値として異なる形式で保存する必要があるため、テンプレートを介してFind()関数をオーバーロード可能として宣言することが理にかなっています。MQL5プログラミング言語では、templateキーワードを使用してこの機能を柔軟かつ非常に便利に実装できます。これにより、typenameを使用したオーバーロード可能なデータ型を持つ関数テンプレートを1回宣言し、コンパイラが各データ型に必要な実装を自動的に置き換えます。カスタムデータ型を渡す予定がないため、異なる型の暗黙的なキャストで問題は発生せず、演算子のオーバーロードをおこなう必要もありません。以下に、Find()カスタム関数テンプレートの実装を示します。

template<typename A>
int               Find(A &aArray[],A aValue)
  {
   for(int i=0; i<ArraySize(aArray); i++)
     {
      if(aArray[i]==aValue)
        {
         return(i);                                                     // The element exists, return the element index
        }
     }
   return(-1);                                                          // No such element, return -1
  }

宣言されたFind()関数テンプレートを使用して、現在のポジションがストレージ内にあるかどうかを確認することでロジックを完了します。関数が-1を返した場合、つまりストレージ内にポジションが存在しない場合は、そのポジションをストレージに追加する必要があります。これを実行するためには、まずストレージのディメンションを変更する必要があります。

                     //--- enter data into the main storage
                     //--- check if there is such id
                     if(Find(PositionID,position_id)==-1)               // if there is no such deal yet,                       {

ストレージにそのような番号が存在する場合、Find()関数によって返されるインデックスを使用してポジションデータにアクセスできます。これは、複数の銘柄が同時にアカウントで取引されているため、取引履歴の注文が異なる順序で配置される可能性があるからです。たとえば、ある銘柄のポジションは、別の銘柄の以前の注文よりも遅く開かれることがあります。そのため、最初の銘柄のポジションよりも早く閉じられる可能性があります。一般的に、期間内のポジションに関する情報を検索し、収集するロジックは以下に要約されています。

      case  Select_period:                                              			// search within a period

         res = MessageBox("You have selected analysis for period. Continue?","",MB_OKCANCEL); 	// wait for user confirmation

         if(res == IDCANCEL)                                            			// if interrupted by user
           {
            printf("%s - %d -> Scrypt was stoped by user.",__FUNCTION__,__LINE__); 		// notify
            return;                                                     			// stop
           }

         MessageBox("Please press 'Ok' and wait for the next message until script will be done."); // notify

         //--- select history data
         if(HistorySelect(start_date,finish_date))                      // select the necessary period in history
           {
            int total = HistoryDealsTotal();                            // got the total number of deals

            if(total <= 0)                                              // if nothing found
              {
               printf("%s - %d -> No deals were found for the specified period.",__FUNCTION__,__LINE__); // notify
               MessageBox("No deals were found for the specified period: "+TimeToString(start_date)+"-"+TimeToString(finish_date)+". Script is done.");
               return;
              }

            for(int i=0; i<total; i++)                                  // iterate through the number of deals
              {
               //--- try to get deals ticket
               if((ticket=HistoryDealGetTicket(i))>0)                   // took the ticket
                 {
                  //--- get deals properties
                  position_id = HistoryDealGetInteger(ticket,DEAL_POSITION_ID);        // took the main id
                  entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket,DEAL_ENTRY);   // entry or exit?
                  type = (int)HistoryDealGetInteger(ticket,DEAL_TYPE);  	       // entry or exit?

                  //--- check the deal type
                  if(type == DEAL_TYPE_BUY            ||                // if it is buy
                     type == DEAL_TYPE_SELL           ||                // if it is sell
                     type == DEAL_TYPE_BUY_CANCELED   ||                // if canceled buy
                     type == DEAL_TYPE_SELL_CANCELED                    // if canceled sell
                    )
                    {
                     //--- is it entry or exit?
                     if(entry == DEAL_ENTRY_IN)                         // if this is an entry
                       {
                        open = HistoryDealGetDouble(ticket,DEAL_PRICE);                	// take open price
                        time_open  =(datetime)HistoryDealGetInteger(ticket,DEAL_TIME); 	// take open time
                        symbol=HistoryDealGetString(ticket,DEAL_SYMBOL);   		// take symbol
                        stop_loss = HistoryDealGetDouble(ticket,DEAL_SL);  		// take Stop Loss
                        take_profit = HistoryDealGetDouble(ticket,DEAL_TP);		// take Take Profit

                        magic = (int)HistoryDealGetInteger(ticket,DEAL_MAGIC);   	// take magic number
                        comment=HistoryDealGetString(ticket,DEAL_COMMENT);       	// take comment
                        externalID=HistoryDealGetString(ticket,DEAL_EXTERNAL_ID);	// take external id
                        volume = HistoryDealGetDouble(ticket,DEAL_VOLUME);          	// take volume
                        commission = HistoryDealGetDouble(ticket,DEAL_COMMISSION);  	// take commission value
                       }

                     if(entry == DEAL_ENTRY_OUT)                        		// if this is an exit
                       {
                        close = HistoryDealGetDouble(ticket,DEAL_PRICE);               	// take close price
                        time_close  =(datetime)HistoryDealGetInteger(ticket,DEAL_TIME);	// take close time

                        reason = (ENUM_DEAL_REASON)HistoryDealGetInteger(ticket,DEAL_REASON); // reason
                        swap = HistoryDealGetDouble(ticket,DEAL_SWAP);        		// swap
                        profit = HistoryDealGetDouble(ticket,DEAL_PROFIT);    		// profit
                        fee = HistoryDealGetDouble(ticket,DEAL_FEE);          		// fee
                       }


                     //--- enter data into the main storage
                     //--- check if there is such id
                     if(Find(PositionID,position_id)==-1)               		// if there is no such deal yet,
                       {
                        //--- change the size of containers
                        ArrayResize(arr_time_open,ArraySize(arr_time_open)+1);       
                        ArrayResize(arr_time_close,ArraySize(arr_time_close)+1);     
                        ArrayResize(arr_symbol,ArraySize(arr_symbol)+1);             
                        ArrayResize(arr_stop_loss,ArraySize(arr_stop_loss)+1);       
                        ArrayResize(arr_take_profit,ArraySize(arr_take_profit)+1);   
                        ArrayResize(arr_open,ArraySize(arr_open)+1);                 
                        ArrayResize(arr_close,ArraySize(arr_close)+1);               
                        ArrayResize(PositionID,ArraySize(PositionID)+1);             

                        ArrayResize(arr_magic,ArraySize(arr_magic)+1);               
                        ArrayResize(arr_extermalID,ArraySize(arr_extermalID)+1);     
                        ArrayResize(arr_comment,ArraySize(arr_comment)+1);           
                        ArrayResize(arr_volume,ArraySize(arr_volume)+1);             
                        ArrayResize(arr_commission,ArraySize(arr_commission)+1);     
                        ArrayResize(arr_reason,ArraySize(arr_reason)+1);             
                        ArrayResize(arr_swap,ArraySize(arr_swap)+1);                 
                        ArrayResize(arr_profit,ArraySize(arr_profit)+1);             
                        ArrayResize(arr_fee,ArraySize(arr_fee)+1);                   

                        PositionID[ArraySize(arr_time_open)-1]=position_id;          


                        if(entry == DEAL_ENTRY_IN)                      		  // if this is an entry,
                          {
                           arr_time_open[    ArraySize(arr_time_open)-1]   = time_open;   // deal time
                           arr_symbol[       ArraySize(arr_symbol)-1]      = symbol;      // instrument symbol
                           arr_stop_loss[    ArraySize(arr_stop_loss)-1]   = stop_loss;   // deal Stop Loss
                           arr_take_profit[  ArraySize(arr_take_profit)-1] = take_profit; // deal Take Profit
                           arr_open[         ArraySize(arr_open)-1]        = open;        // open price
                           //---
                           arr_magic[        ArraySize(arr_magic)-1]       = magic;       // magic number
                           arr_comment[      ArraySize(arr_comment)-1]     = comment;     // comment
                           arr_extermalID[   ArraySize(arr_extermalID)-1]  = externalID;  // external id
                           arr_volume[       ArraySize(arr_volume)-1]      = volume;      // volume
                           arr_commission[   ArraySize(arr_commission)-1]  = commission;  // commission
                          }

                        if(entry == DEAL_ENTRY_OUT)                     		  // if this is an exit
                          {
                           arr_time_close[   ArraySize(arr_time_close)-1]  = time_close;  // close time
                           arr_close[        ArraySize(arr_close)-1]       = close;       // close price
                           //---
                           arr_reason[       ArraySize(arr_reason)-1]      = reason;      // reason
                           arr_swap[         ArraySize(arr_swap)-1]        = swap;        // swap
                           arr_profit[       ArraySize(arr_profit)-1]      = profit;      // profit
                           arr_fee[          ArraySize(arr_fee)-1]         = fee;         // fee
                          }
                       }
                     else
                       {
                        int index = Find(PositionID,position_id);       // if found, search for the index

                        if(entry == DEAL_ENTRY_IN)                      // if this is an entry
                          {
                           arr_time_open[index]   = time_open;          // deal time
                           arr_symbol[index]      = symbol;             // symbol
                           arr_stop_loss[index]   = stop_loss;          // deal Stop Loss
                           arr_take_profit[index] = take_profit;        // deal Take Profit
                           arr_open[index]        = open;               // close price
                           //---
                           arr_magic[index]       = magic;              // magic number
                           arr_comment[index]     = comment;            // comment
                           arr_extermalID[index]  = externalID;         // external id
                           arr_volume[index]      = volume;             // volume
                           arr_commission[index]  = commission;         // commission
                          }

                        if(entry == DEAL_ENTRY_OUT)                     // if this is an exit
                          {
                           arr_time_close[index]  = time_close;         // deal close time
                           arr_close[index]       = close;              // deal close price
                           //---
                           arr_reason[index]      = reason;             // reason
                           arr_swap[index]        = swap;               // swap
                           arr_profit[index]      = profit;             // profit
                           arr_fee[index]         = fee;                // fee
                          }
                       }
                    }
                 }
              }
           }
         else
           {
            printf("%s - %d -> Error of selecting history deals: %d",__FUNCTION__,__LINE__,GetLastError()); // notify
            printf("%s - %d -> No deals were found for the specified period.",__FUNCTION__,__LINE__);       // notify
            MessageBox("No deals were found for the specified period: "+TimeToString(start_date)+"-"+TimeToString(finish_date)+". Script is done.");
            return;
           }
         break;


第一回の結論

この記事では、金融市場で安全かつ長期的な取引をおこなうための過去の取引分析の重要性について検討しました。この分析において重要な要素は、スクリプトに実装し始めた過去の取引の研究です。入力の形成や、選択した期間内の取引履歴データを選択するためのアルゴリズムの実装を考察しました。また、データコンテナの処理を簡素化するために、オーバーロード可能な関数テンプレートを実装しました。

次回の記事では、単一の取引データを選択するアルゴリズムを考慮しながらスクリプトを完成させ、チャートを描画し、データオブジェクトをチャート上に表示するためのコードを実装する予定です。

MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/14903

添付されたファイル |
DealsPrintScreen.mq5 (104.52 KB)
ニュース取引が簡単に(第2回):リスク管理 ニュース取引が簡単に(第2回):リスク管理
この記事では、以前のコードと新しいコードに継承を導入します。効率性を高めるために新しいデータベース設計が実装されます。さらに、取引量計算に取り組むためのリスク管理クラスも作成されます。
PythonとMQL5でロボットを開発する(第2回):モデルの選択、作成、訓練、Pythonカスタムテスター PythonとMQL5でロボットを開発する(第2回):モデルの選択、作成、訓練、Pythonカスタムテスター
PythonとMQL5で自動売買ロボットを開発する連載を続けます。今日は、モデルの選択と訓練、テスト、交差検証、グリッドサーチ、モデルアンサンブルの問題を解決します。
古典的な戦略を再構築する(第7回):USDJPYにおける外国為替市場とソブリン債務分析 古典的な戦略を再構築する(第7回):USDJPYにおける外国為替市場とソブリン債務分析
本日の記事では、今後の為替レートと国債の関係を分析します。債券は、最も人気のある固定利付証券の1つであり、今回の議論の焦点となります。AIを使用して従来の戦略を改善できるかどうかを一緒に検討しましょう。
ニューラルネットワークが簡単に(第90回):時系列の周波数補間(FITS) ニューラルネットワークが簡単に(第90回):時系列の周波数補間(FITS)
FEDformer法を研究することで、時系列表現の周波数領域への扉を開きました。この新しい記事では、私たちが始めたトピックを続けます。分析をおこなうだけでなく、特定の分野におけるその後の状態を予測することができる手法について考えてみたいと思います。