English Русский Deutsch
preview
過去のポジションをチャート上に損益図として表示する

過去のポジションをチャート上に損益図として表示する

MetaTrader 5 | 25 4月 2024, 15:45
257 0
Artyom Trishkin
Artyom Trishkin

内容


はじめに

ポジションとは、サーバーに送られた取引注文を実行した結果です。つまり、注文→取引→ポジションです。

ポジションが開いている銘柄名を示しながら、ネットポジション勘定の場合、PositionSelect()関数を使用して、プログラムで常に未決済ポジションのリストを取得することができます。

   if(PositionSelect(symbol_name))
     {
      /*
      Work with selected position data
      PositionGetDouble();
      PositionGetInteger();
      PositionGetString();
      */
     }

独立したポジション表示(ヘッジ)を持つ勘定については、まずPositionsTotal()を使用してポジション数を取得します。次に、ポジション数によるループで、PositionGetTicket()を使用して、未決済リスト内のインデックスによってポジションチケットを取得します。その後、PositionSelectByTicket()を使用して、受信したチケットでポジションを選択します。

   int total=PositionsTotal();
   for(int i=total-1;i>=0;i--)
     {
      ulong ticket=PositionGetTicket(i);
      if(ticket==0 || !PositionSelectByTicket(ticket))
         continue;
      /*
      Work with selected position data
      PositionGetDouble();
      PositionGetInteger();
      PositionGetString();
      */
     }

ここではすべてがシンプルで明快です。しかし、すでに閉じたポジションについて何かを調べる必要がある場合、これはまったく別の問題です。過去のポジションを操作する関数はありません...

この場合、各ポジションがそれぞれ固有のIDを持っていることを覚えておく必要があります。このIDは、ポジションに影響を与えた取引、つまりポジションの開始、変更、終了に関与した取引に登録されます。HistorySelect()HistoryDealsTotal()HistoryDealGetTicket()(HistoryDealSelect())関数を使用して、取引のリスト(取引と、その取引が参加したポジションのIDを含む)を取得できます。

一般的に、その取引が関与したポジションのIDは、以下のようにして取得できます。

   if(HistorySelect(0,TimeCurrent()))
     {
      int total=HistoryDealsTotal();
      for(int i=0;i<total;i++)
        {
         ulong ticket=HistoryDealGetTicket(i);
         if(ticket==0)
            continue;
         long pos_id=HistoryDealGetInteger(ticket,DEAL_POSITION_ID); // ID of a position a deal participated in
         /*
         Work with selected deal data
         HistoryDealGetDouble();
         HistoryDealGetInteger();
         HistoryDealGetString();
         */
        }
     }

つまり、取引のリストがあれば、どのポジションがどの取引に属していたかをいつでも知ることができます。例えば、ポジションIDを取得し、同じIDを持つ取引を見て、市場エントリとエグジットの取引を見つけることができます。これらの取引の特性を利用することで、時間、ロット、その他必要な過去ポジションの特性を見つけることができます。

閉じた過去のポジションを検索するには、過去の取引リストを取得します。このリストによるループの中で、次の各取引を取得し、その取引が関与したポジションIDを確認します。もしそのようなポジションがまだリストになければ、ポジションオブジェクトを作成し、すでに存在すれば、同じIDを持つ既存のポジションを使用します。現在の取引がまだリストにない場合、見つかったポジションの取引リストに追加します。このように、過去の取引の全リストを調べた後、取引が関与したすべてのポジションを見つけ、過去の全ポジションのリストを作成し、このポジションに関与したすべての取引を過去ポジションの各オブジェクトに追加します(すなわち、その取引のリストに追加する)。

3つのクラスが必要です。

  1. 取引クラス:ポジションとそのプロパティを識別するために必要な取引プロパティを含む
  2. ポジションクラス:ポジションに関与した取引のリストと、ポジション固有のプロパティを含む
  3. 過去ポジションリストクラス:検索された過去ポジションのリストで、指定されたプロパティに基づいてポジションを選択できる

これら3つの小さなクラスを使用すると、過去の取引をすべて簡単に見つけ、リストに保存し、指標でこれらのポジションのデータを使用して、口座で選択した銘柄のポジションの損益図を描くことができます。

Indicatorsフォルダに、PositionInfoIndicatorという名前の指標ファイルを新規作成します。1つの描画可能なバッファを持つ指標を別のチャートウィンドウに塗りつぶしスタイルで緑と赤の塗りつぶし色で描画することを指定します。

以下のヘッダーを持つ指標テンプレートが作成されます。

//+------------------------------------------------------------------+
//|                                        PositionInfoIndicator.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   1
//--- plot Fill
#property indicator_label1  "Profit;ZeroLine"
#property indicator_type1   DRAW_FILLING
#property indicator_color1  clrGreen,clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

作成するクラスを以下に入力します。

取引とポジションのリストを作成するには、スタンダードライブラリCObjectクラスとその子孫クラスのインスタンスへのポインタの動的配列クラスを使用します。

作成したファイルにクラスのファイルをインクルードします。

//+------------------------------------------------------------------+
//|                                        PositionInfoIndicator.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   1
//--- plot Fill
#property indicator_label1  "Profit;ZeroLine"
#property indicator_type1   DRAW_FILLING
#property indicator_color1  clrGreen,clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- includes
#include <Arrays\ArrayObj.mqh>

このクラスにはSearch()メソッドがあり、並び替えられた配列の中から例に等しい要素を探します。

//+------------------------------------------------------------------+
//| Search of position of element in a sorted array                  |
//+------------------------------------------------------------------+
int CArrayObj::Search(const CObject *element) const
  {
   int pos;
//--- check
   if(m_data_total==0 || !CheckPointer(element) || m_sort_mode==-1)
      return(-1);
//--- search
   pos=QuickSearch(element);
   if(m_data[pos].Compare(element,m_sort_mode)==0)
      return(pos);
//--- not found
   return(-1);
  }

配列は、ポインタを含むオブジェクトのプロパティによって並び替えられなければなりません。並び替えモードは、m_sort_mode変数に整数を渡して設定します。デフォルトでは0が使用されます。値「-1」は、配列が並び替えられていないことを示します。異なるプロパティで並び替えるには、m_sort_mode変数に0以上の値を設定して、異なる並び替えモードを設定する必要があります。そのためには、リストのさまざまな並び替えモードを定義する列挙型を使用すると便利です。これらのモードは、2つのCompare()オブジェクトを比較する仮想メソッドで使用されます。これはCObjectクラスで定義されて、0の値を返します。これは、比較されるオブジェクトが同一であることを意味します。

   //--- method of comparing the objects
   virtual int       Compare(const CObject *node,const int mode=0) const { return(0);      }

このメソッドはカスタムクラスでオーバーライドされるべきで、そこでは、同じ型の2つのオブジェクトの比較を、それらのさまざまなプロパティに従ってアレンジすることができます(m_sort_modeの値が0に等しい場合、オブジェクトは1つのプロパティによって比較され、1に等しい場合は別のプロパティによって、2に等しい場合は3番目のプロパティによって、など)。

ここで作成される各クラスオブジェクトは、それぞれ独自のプロパティセットを持ちます。オブジェクトは、検索を実行するために、これらのプロパティで並び替える必要があります。したがって、まずオブジェクトプロパティの列挙を作成する必要があります。

//+------------------------------------------------------------------+
//|                                        PositionInfoIndicator.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   1
//--- plot Fill
#property indicator_label1  "Profit;ZeroLine"
#property indicator_type1   DRAW_FILLING
#property indicator_color1  clrGreen,clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- includes
#include <Arrays\ArrayObj.mqh>
//--- enums
//--- Deal object sorting modes
enum ENUM_DEAL_SORT_MODE
  {
   DEAL_SORT_MODE_TIME_MSC,   // Deal execution time in milliseconds
   DEAL_SORT_MODE_TIME,       // Deal execution time
   DEAL_SORT_MODE_TIKET,      // Deal ticket
   DEAL_SORT_MODE_POS_ID,     // Position ID
   DEAL_SORT_MODE_MAGIC,      // Magic number for a deal
   DEAL_SORT_MODE_TYPE,       // Deal type
   DEAL_SORT_MODE_ENTRY,      // Deal entry - entry in, entry out, reverse
   DEAL_SORT_MODE_VOLUME,     // Deal volume
   DEAL_SORT_MODE_PRICE,      // Deal price
   DEAL_SORT_MODE_COMISSION,  // Deal commission
   DEAL_SORT_MODE_SWAP,       // Accumulated swap when closing
   DEAL_SORT_MODE_PROFIT,     // Deal financial result
   DEAL_SORT_MODE_FEE,        // Deal fee
   DEAL_SORT_MODE_SYMBOL,     // Name of the symbol for which the deal is executed
  };
//--- Position object sorting modes
enum ENUM_POS_SORT_MODE
  {
   POS_SORT_MODE_TIME_IN_MSC, // Open time in milliseconds
   POS_SORT_MODE_TIME_OUT_MSC,// Close time in milliseconds
   POS_SORT_MODE_TIME_IN,     // Open time
   POS_SORT_MODE_TIME_OUT,    // Close time
   POS_SORT_MODE_DEAL_IN,     // Open deal ticket
   POS_SORT_MODE_DEAL_OUT,    // Close deal ticket
   POS_SORT_MODE_ID,          // Position ID
   POS_SORT_MODE_MAGIC,       // Position magic
   POS_SORT_MODE_PRICE_IN,    // Open price
   POS_SORT_MODE_PRICE_OUT,   // Close price
   POS_SORT_MODE_VOLUME,      // Position volume
   POS_SORT_MODE_SYMBOL,      // Position symbol
  };  
//--- classes

これら2つの列挙型の定数に対応するプロパティは、取引とポジションのオブジェクトクラスに含まれます。これらのプロパティによってリストを並び替え、オブジェクトの等質性を見つけることが可能になります。

以下で、作成したクラスのコードを入力します。


    取引クラス

    取引クラス全体を見てみましょう。
    //+------------------------------------------------------------------+
    //| Deal class                                                       |
    //+------------------------------------------------------------------+
    class CDeal : public CObject
      {
    private:
       long              m_ticket;                        // Deal ticket
       long              m_magic;                         // Magic number for a deal
       long              m_position_id;                   // Position ID
       long              m_time_msc;                      // Deal execution time in milliseconds
       datetime          m_time;                          // Deal execution time
       ENUM_DEAL_TYPE    m_type;                          // Deal type
       ENUM_DEAL_ENTRY   m_entry;                         // Deal entry - entry in, entry out, reverse
       double            m_volume;                        // Deal volume
       double            m_price;                         // Deal price
       double            m_comission;                     // Deal commission
       double            m_swap;                          // Accumulated swap when closing
       double            m_profit;                        // Deal financial result
       double            m_fee;                           // Deal fee
       string            m_symbol;                        // Name of the symbol for which the deal is executed
    //--- Return the deal direction description
       string            EntryDescription(void) const
                           {
                            return(this.m_entry==DEAL_ENTRY_IN ? "Entry In" : this.m_entry==DEAL_ENTRY_OUT ? "Entry Out" : this.m_entry==DEAL_ENTRY_INOUT ? "Reverce" : "Close a position by an opposite one");
                           }
    //--- Return the deal type description
       string            TypeDescription(void) const
                           {
                            switch(this.m_type)
                              {
                               case DEAL_TYPE_BUY                     :  return "Buy";
                               case DEAL_TYPE_SELL                    :  return "Sell";
                               case DEAL_TYPE_BALANCE                 :  return "Balance";
                               case DEAL_TYPE_CREDIT                  :  return "Credit";
                               case DEAL_TYPE_CHARGE                  :  return "Additional charge";
                               case DEAL_TYPE_CORRECTION              :  return "Correction";
                               case DEAL_TYPE_BONUS                   :  return "Bonus";
                               case DEAL_TYPE_COMMISSION              :  return "Additional commission";
                               case DEAL_TYPE_COMMISSION_DAILY        :  return "Daily commission";
                               case DEAL_TYPE_COMMISSION_MONTHLY      :  return "Monthly commission";
                               case DEAL_TYPE_COMMISSION_AGENT_DAILY  :  return "Daily agent commission";
                               case DEAL_TYPE_COMMISSION_AGENT_MONTHLY:  return "Monthly agent commission";
                               case DEAL_TYPE_INTEREST                :  return "Interest rate";
                               case DEAL_TYPE_BUY_CANCELED            :  return "Canceled buy deal";
                               case DEAL_TYPE_SELL_CANCELED           :  return "Canceled sell deal";
                               case DEAL_DIVIDEND                     :  return "Dividend operations";
                               case DEAL_DIVIDEND_FRANKED             :  return "Franked (non-taxable) dividend operations";
                               case DEAL_TAX                          :  return "Tax charges";
                               default                                :  return "Unknown: "+(string)this.m_type;
                              }
                           }
    //--- Return time with milliseconds
       string            TimeMSCtoString(const long time_msc,int flags=TIME_DATE|TIME_MINUTES|TIME_SECONDS)
                           {
                            return ::TimeToString(time_msc/1000,flags)+"."+::IntegerToString(time_msc%1000,3,'0');
                           }
    public:
    //--- Methods for returning deal properties
       long              Ticket(void)                     const { return this.m_ticket;       }  // Deal ticket
       long              Magic(void)                      const { return this.m_magic;        }  // Magic number for a deal
       long              PositionID(void)                 const { return this.m_position_id;  }  // Position ID
       long              TimeMsc(void)                    const { return this.m_time_msc;     }  // Deal execution time in milliseconds
       datetime          Time(void)                       const { return this.m_time;         }  // Deal execution time
       ENUM_DEAL_TYPE    TypeDeal(void)                   const { return this.m_type;         }  // Deal type
       ENUM_DEAL_ENTRY   Entry(void)                      const { return this.m_entry;        }  // Deal entry - entry in, entry out, reverse
       double            Volume(void)                     const { return this.m_volume;       }  // Deal volume
       double            Price(void)                      const { return this.m_price;        }  // Deal price
       double            Comission(void)                  const { return this.m_comission;    }  // Deal commission
       double            Swap(void)                       const { return this.m_swap;         }  // Accumulated swap when closing
       double            Profit(void)                     const { return this.m_profit;       }  // Deal financial result
       double            Fee(void)                        const { return this.m_fee;          }  // Deal fee
       string            Symbol(void)                     const { return this.m_symbol;       }  // Name of the symbol, for which the deal is executed
    //--- Methods for setting deal properties
       void              SetTicket(const long ticket)           { this.m_ticket=ticket;       }  // Deal ticket
       void              SetMagic(const long magic)             { this.m_magic=magic;         }  // Magic number for a deal
       void              SetPositionID(const long id)           { this.m_position_id=id;      }  // Position ID
       void              SetTimeMsc(const long time_msc)        { this.m_time_msc=time_msc;   }  // Deal execution time in milliseconds
       void              SetTime(const datetime time)           { this.m_time=time;           }  // Deal execution time
       void              SetType(const ENUM_DEAL_TYPE type)     { this.m_type=type;           }  // Deal type
       void              SetEntry(const ENUM_DEAL_ENTRY entry)  { this.m_entry=entry;         }  // Deal entry - entry in, entry out, reverse
       void              SetVolume(const double volume)         { this.m_volume=volume;       }  // Deal volume
       void              SetPrice(const double price)           { this.m_price=price;         }  // Deal price
       void              SetComission(const double comission)   { this.m_comission=comission; }  // Deal commission
       void              SetSwap(const double swap)             { this.m_swap=swap;           }  // Accumulated swap when closing
       void              SetProfit(const double profit)         { this.m_profit=profit;       }  // Deal financial result
       void              SetFee(const double fee)               { this.m_fee=fee;             }  // Deal fee
       void              SetSymbol(const string symbol)         { this.m_symbol=symbol;       }  // Name of the symbol, for which the deal is executed
    //--- Method for comparing two objects
       virtual int       Compare(const CObject *node,const int mode=0) const
                           {
                            const CDeal *compared_obj=node;
                            switch(mode)
                              {
                               case DEAL_SORT_MODE_TIME      :  return(this.Time()>compared_obj.Time()             ?  1  :  this.Time()<compared_obj.Time()              ?  -1 :  0);
                               case DEAL_SORT_MODE_TIME_MSC  :  return(this.TimeMsc()>compared_obj.TimeMsc()       ?  1  :  this.TimeMsc()<compared_obj.TimeMsc()        ?  -1 :  0);
                               case DEAL_SORT_MODE_TIKET     :  return(this.Ticket()>compared_obj.Ticket()         ?  1  :  this.Ticket()<compared_obj.Ticket()          ?  -1 :  0);
                               case DEAL_SORT_MODE_MAGIC     :  return(this.Magic()>compared_obj.Magic()           ?  1  :  this.Magic()<compared_obj.Magic()            ?  -1 :  0);
                               case DEAL_SORT_MODE_POS_ID    :  return(this.PositionID()>compared_obj.PositionID() ?  1  :  this.PositionID()<compared_obj.PositionID()  ?  -1 :  0);
                               case DEAL_SORT_MODE_TYPE      :  return(this.TypeDeal()>compared_obj.TypeDeal()     ?  1  :  this.TypeDeal()<compared_obj.TypeDeal()      ?  -1 :  0);
                               case DEAL_SORT_MODE_ENTRY     :  return(this.Entry()>compared_obj.Entry()           ?  1  :  this.Entry()<compared_obj.Entry()            ?  -1 :  0);
                               case DEAL_SORT_MODE_VOLUME    :  return(this.Volume()>compared_obj.Volume()         ?  1  :  this.Volume()<compared_obj.Volume()          ?  -1 :  0);
                               case DEAL_SORT_MODE_PRICE     :  return(this.Price()>compared_obj.Price()           ?  1  :  this.Price()<compared_obj.Price()            ?  -1 :  0);
                               case DEAL_SORT_MODE_COMISSION :  return(this.Comission()>compared_obj.Comission()   ?  1  :  this.Comission()<compared_obj.Comission()    ?  -1 :  0);
                               case DEAL_SORT_MODE_SWAP      :  return(this.Swap()>compared_obj.Swap()             ?  1  :  this.Swap()<compared_obj.Swap()              ?  -1 :  0);
                               case DEAL_SORT_MODE_PROFIT    :  return(this.Profit()>compared_obj.Profit()         ?  1  :  this.Profit()<compared_obj.Profit()          ?  -1 :  0);
                               case DEAL_SORT_MODE_FEE       :  return(this.Fee()>compared_obj.Fee()               ?  1  :  this.Fee()<compared_obj.Fee()                ?  -1 :  0);
                               case DEAL_SORT_MODE_SYMBOL    :  return(this.Symbol()>compared_obj.Symbol()         ?  1  :  this.Symbol()<compared_obj.Symbol()          ?  -1 :  0);
                               default                       :  return(this.TimeMsc()>compared_obj.TimeMsc()       ?  1  :  this.TimeMsc()<compared_obj.TimeMsc()        ?  -1 :  0);
                              }
                           }
                           
    //--- Print deal properties in the journal
       void              Print(void)
                           {
                            ::PrintFormat("  Deal: %s type %s #%lld at %s",this.EntryDescription(),this.TypeDescription(),this.Ticket(),this.TimeMSCtoString(this.TimeMsc()));
                           }
    //--- Constructor
                         CDeal(const long deal_ticket)
                           {
                            this.m_ticket=deal_ticket;
                            this.m_magic=::HistoryDealGetInteger(deal_ticket,DEAL_MAGIC);                    // Magic number for a deal
                            this.m_position_id=::HistoryDealGetInteger(deal_ticket,DEAL_POSITION_ID);        // Position ID
                            this.m_time_msc=::HistoryDealGetInteger(deal_ticket,DEAL_TIME_MSC);              // Deal execution time in milliseconds
                            this.m_time=(datetime)::HistoryDealGetInteger(deal_ticket,DEAL_TIME);            // Deal execution time
                            this.m_type=(ENUM_DEAL_TYPE)::HistoryDealGetInteger(deal_ticket,DEAL_TYPE);      // Deal type
                            this.m_entry=(ENUM_DEAL_ENTRY)::HistoryDealGetInteger(deal_ticket,DEAL_ENTRY);   // Deal entry - entry in, entry out, reverse
                            this.m_volume=::HistoryDealGetDouble(deal_ticket,DEAL_VOLUME);                   // Deal volume
                            this.m_price=::HistoryDealGetDouble(deal_ticket,DEAL_PRICE);                     // Deal volume
                            this.m_comission=::HistoryDealGetDouble(deal_ticket,DEAL_COMMISSION);            // Deal commission
                            this.m_swap=::HistoryDealGetDouble(deal_ticket,DEAL_SWAP);                       // Accumulated swap when closing
                            this.m_profit=::HistoryDealGetDouble(deal_ticket,DEAL_PROFIT);                   // Deal financial result
                            this.m_fee=::HistoryDealGetDouble(deal_ticket,DEAL_FEE);                         // Deal fee
                            this.m_symbol=::HistoryDealGetString(deal_ticket,DEAL_SYMBOL);                   // Name of the symbol, for which the deal is executed
                           }
                        ~CDeal(void){}
      };
    
    

    ここではすべてが非常に簡単です。取引プロパティを格納するクラスメンバー変数はprivateセクションで宣言されます。publicセクションは、取引のプロパティを設定したり返したりするメソッドを実装しています。Compare()仮想メソッドは、比較モードとしてどのプロパティがメソッドに渡されるかに応じて、各取引プロパティの比較を実装します。現在のオブジェクトの確認対象のプロパティの値が、比較対象のオブジェクトの同じプロパティの値より大きければ1が返され、小さければ-1が返されます。値が等しい場合は0となります。クラスコンストラクタでは、その取引はすでに選択されているとみなされ、そのプロパティは対応するprivateクラス変数に書き込まれます。端末の過去の取引リストから選択された取引の必要なすべてのプロパティを格納する取引オブジェクトを作成するのには、これですでに十分です。クラスオブジェクトは各取引ごとに作成されます。オブジェクトのプロパティからポジションIDが抽出され、過去ポジションクラスのオブジェクトが作成されます。このポジションに属するすべての取引は、ポジションクラスの取引リストに表示されます。従って、過去ポジションオブジェクトには、そのすべての取引のリストが含まれることになります。そこからポジション履歴を抽出することが可能になります。

    過去ポジションクラスを考えてみましょう。

    ポジションクラス

    ポジションクラスは、このポジションの取引のリストと、ポジションのライフタイムに関する情報を取得したり返したりする値を計算するための補助メソッドを含みます。

    //+------------------------------------------------------------------+
    //| Position class                                                   |
    //+------------------------------------------------------------------+
    class CPosition : public CObject
      {
    private:
       CArrayObj         m_list_deals;                    // List of position deals
       long              m_position_id;                   // Position ID
       long              m_time_in_msc;                   // Open time in milliseconds
       long              m_time_out_msc;                  // Close time in milliseconds
       long              m_magic;                         // Position magic
       datetime          m_time_in;                       // Open time
       datetime          m_time_out;                      // Close time
       ulong             m_deal_in_ticket;                // Open deal ticket
       ulong             m_deal_out_ticket;               // Close deal ticket
       double            m_price_in;                      // Open price
       double            m_price_out;                     // Close price
       double            m_volume;                        // Position volume
       ENUM_POSITION_TYPE m_type;                         // Position type
       string            m_symbol;                        // Position symbol
       int               m_digits;                        // Symbol digits
       double            m_point;                         // One symbol point value
       double            m_contract_size;                 // Symbol trade contract size
       string            m_currency_profit;               // Symbol profit currency
       string            m_account_currency;              // Deposit currency
    //--- Return time with milliseconds
       string            TimeMSCtoString(const long time_msc,int flags=TIME_DATE|TIME_MINUTES|TIME_SECONDS)
                           {
                            return ::TimeToString(time_msc/1000,flags)+"."+::IntegerToString(time_msc%1000,3,'0');
                           }
    //--- Calculate and return the open time of a known bar on a specified chart period by a specified time
    //--- (https://www.mql5.com/ru/forum/170952/page234#comment_50523898)
       datetime          BarOpenTime(const ENUM_TIMEFRAMES timeframe,const datetime time) const
                           {
                            ENUM_TIMEFRAMES period=(timeframe==PERIOD_CURRENT ? ::Period() : timeframe);
                            //--- Calculate the bar open time on periods less than W1
                            if(period<PERIOD_W1)
                               return time-time%::PeriodSeconds(period);
                            //--- Calculate the bar open time on W1
                            if(period==PERIOD_W1)
                               return time-(time+4*24*60*60)%::PeriodSeconds(period);
                            //--- Calculate the bar open time on MN1
                            else
                              {
                               MqlDateTime dt;
                               ::ResetLastError();
                               if(!::TimeToStruct(time,dt))
                                 {
                                  ::PrintFormat("%s: TimeToStruct failed. Error %lu",__FUNCTION__,::GetLastError());
                                  return 0;
                                 }
                               return time-(time%(24*60*60))-(dt.day-1)*(24*60*60);
                              }
                           }
    //--- Return the symbol availability flag on the server. Add a symbol to MarketWatch window
       bool              SymbolIsExist(const string symbol) const
                           {
                            bool custom=false;
                            if(!::SymbolExist(symbol,custom))
                               return false;
                            return ::SymbolSelect(symbol,true);
                           }
    //--- Return the price of one point
       double            GetOnePointPrice(const datetime time) const
                           {
                            if(time==0)
                               return 0;
                            //--- If the symbol's profit currency matches the account currency, return the contract size * Point of the symbol
                            if(this.m_currency_profit==this.m_account_currency)
                               return this.m_point*this.m_contract_size;
                            //--- Otherwise, check for the presence of a symbol with the name "Account currency" + "Symbol profit currency" 
                            double array[1];
                            string reverse=this.m_account_currency+this.m_currency_profit;
                            //--- If such a symbol exists and is added to the Market Watch
                            if(this.SymbolIsExist(reverse))
                              {
                               //--- If the closing price of the bar by 'time' is received, return the contract size * Point of the symbol divided by the Close price of the bar 
                               if(::CopyClose(reverse,PERIOD_CURRENT,time,1,array)==1 && array[0]>0)
                                  return this.m_point*this.m_contract_size/array[0];
                               //--- If failed to get the closing price of the bar by 'time', return zero
                               else
                                  return 0;
                              }
                            //--- Check for the presence of a symbol with the name "Symbol profit currency" + "Account currency"
                            string direct=this.m_currency_profit+this.m_account_currency;
                            //--- If such a symbol exists and is added to the Market Watch
                            if(this.SymbolIsExist(direct))
                              {
                               //--- If the closing price of the bar by 'time' is received, return the contract size * Point of the symbol multiplied by the Close price of the bar
                               if(::CopyClose(direct,PERIOD_CURRENT,time,1,array)==1)
                                  return this.m_point*this.m_contract_size*array[0];
                              }
                            //--- Failed to get symbols whose profit currency does not match the account currency, neither reverse nor direct - return zero
                            return 0;
                           }
    public:
    //--- Methods for returning position properties
       long              ID(void)                         const { return this.m_position_id;              }  // Position ID 
       long              Magic(void)                      const { return this.m_magic;                    }  // Position magic 
       long              TimeInMsc(void)                  const { return this.m_time_in_msc;              }  // Open time
       long              TimeOutMsc(void)                 const { return this.m_time_out_msc;             }  // Close time
       datetime          TimeIn(void)                     const { return this.m_time_in;                  }  // Open time
       datetime          TimeOut(void)                    const { return this.m_time_out;                 }  // Close time
       ulong             DealIn(void)                     const { return this.m_deal_in_ticket;           }  // Open deal ticket
       ulong             DealOut(void)                    const { return this.m_deal_out_ticket;          }  // Close deal ticket
       ENUM_POSITION_TYPE TypePosition(void)              const { return this.m_type;                     }  // Position type
       double            PriceIn(void)                    const { return this.m_price_in;                 }  // Open price
       double            PriceOut(void)                   const { return this.m_price_out;                }  // Close price
       double            Volume(void)                     const { return this.m_volume;                   }  // Position volume
       string            Symbol(void)                     const { return this.m_symbol;                   }  // Position symbol
    //--- Methods for setting position properties
       void              SetID(long id)                         { this.m_position_id=id;                  }  // Position ID
       void              SetMagic(long magic)                   { this.m_magic=magic;                     }  // Position magic
       void              SetTimeInMsc(long time_in_msc)         { this.m_time_in_msc=time_in_msc;         }  // Open time
       void              SetTimeOutMsc(long time_out_msc)       { this.m_time_out_msc=time_out_msc;       }  // Close time
       void              SetTimeIn(datetime time_in)            { this.m_time_in=time_in;                 }  // Open time
       void              SetTimeOut(datetime time_out)          { this.m_time_out=time_out;               }  // Close time
       void              SetDealIn(ulong ticket_deal_in)        { this.m_deal_in_ticket=ticket_deal_in;   }  // Open deal ticket
       void              SetDealOut(ulong ticket_deal_out)      { this.m_deal_out_ticket=ticket_deal_out; }  // Close deal ticket
       void              SetType(ENUM_POSITION_TYPE type)       { this.m_type=type;                       }  // Position type
       void              SetPriceIn(double price_in)            { this.m_price_in=price_in;               }  // Open price
       void              SetPriceOut(double price_out)          { this.m_price_out=price_out;             }  // Close price
       void              SetVolume(double new_volume)           { this.m_volume=new_volume;               }  // Position volume
       void              SetSymbol(string symbol)                                                            // Position symbol
                           {
                            this.m_symbol=symbol;
                            this.m_digits=(int)::SymbolInfoInteger(this.m_symbol,SYMBOL_DIGITS);
                            this.m_point=::SymbolInfoDouble(this.m_symbol,SYMBOL_POINT);
                            this.m_contract_size=::SymbolInfoDouble(this.m_symbol,SYMBOL_TRADE_CONTRACT_SIZE);
                            this.m_currency_profit=::SymbolInfoString(this.m_symbol,SYMBOL_CURRENCY_PROFIT);
                           }
    //--- Add a deal to the list of deals
       bool              DealAdd(CDeal *deal)
                           {
                            //--- Declare a variable of the result of adding a deal to the list
                            bool res=false;
                            //--- Set the flag of sorting by deal ticket for the list
                            this.m_list_deals.Sort(DEAL_SORT_MODE_TIKET);
                            //--- If a deal with such a ticket is not in the list -
                            if(this.m_list_deals.Search(deal)==WRONG_VALUE)
                              {
                               //--- Set the flag of sorting by time in milliseconds for the list and
                               //--- return the result of adding a deal to the list in order of sorting by time
                               this.m_list_deals.Sort(DEAL_SORT_MODE_TIME_MSC);
                               res=this.m_list_deals.InsertSort(deal);
                              }
                            //--- If the deal is already in the list, return 'false'
                            else
                               this.m_list_deals.Sort(DEAL_SORT_MODE_TIME_MSC);
                            return res;
                           }
    //--- Returns the start time of the bar (1) opening, (2) closing a position on the current chart period
       datetime          BarTimeOpenPosition(void) const
                           {
                            return this.BarOpenTime(PERIOD_CURRENT,this.TimeIn());
                           }
       datetime          BarTimeClosePosition(void) const
                           {
                            return this.BarOpenTime(PERIOD_CURRENT,this.TimeOut());
                           }
    //--- Return the flag of the existence of a position at the specified time
       bool              IsPresentInTime(const datetime time) const
                           {
                            return(time>=this.BarTimeOpenPosition() && time<=this.BarTimeClosePosition());
                           }
    //--- Return the profit of the position in the number of points or in the value of the number of points relative to the close price
       double            ProfitRelativeClosePrice(const double price_close,const datetime time,const bool points) const
                           {
                            //--- If there was no position at the specified time, return 0
                            if(!this.IsPresentInTime(time))
                               return 0;
                            //--- Calculate the number of profit points depending on the position direction
                            int pp=int((this.TypePosition()==POSITION_TYPE_BUY ? price_close-this.PriceIn() : this.PriceIn()-price_close)/this.m_point);
                            //--- If the profit is in points, return the calculated number of points
                            if(points)
                               return pp;
                            //--- Otherwise, return the calculated number of points multiplied by (cost of one point * position volume)
                            return pp*this.GetOnePointPrice(time)*this.Volume();
                           }
    //--- Method for comparing two objects
       virtual int       Compare(const CObject *node,const int mode=0) const
                           {
                            const CPosition *compared_obj=node;
                            switch(mode)
                              {
                               case POS_SORT_MODE_TIME_IN_MSC   :  return(this.TimeInMsc()>compared_obj.TimeInMsc()   ?  1  :  this.TimeInMsc()<compared_obj.TimeInMsc()    ?  -1 :  0);
                               case POS_SORT_MODE_TIME_OUT_MSC  :  return(this.TimeOutMsc()>compared_obj.TimeOutMsc() ?  1  :  this.TimeOutMsc()<compared_obj.TimeOutMsc()  ?  -1 :  0);
                               case POS_SORT_MODE_TIME_IN       :  return(this.TimeIn()>compared_obj.TimeIn()         ?  1  :  this.TimeIn()<compared_obj.TimeIn()          ?  -1 :  0);
                               case POS_SORT_MODE_TIME_OUT      :  return(this.TimeOut()>compared_obj.TimeOut()       ?  1  :  this.TimeOut()<compared_obj.TimeOut()        ?  -1 :  0);
                               case POS_SORT_MODE_DEAL_IN       :  return(this.DealIn()>compared_obj.DealIn()         ?  1  :  this.DealIn()<compared_obj.DealIn()          ?  -1 :  0);
                               case POS_SORT_MODE_DEAL_OUT      :  return(this.DealOut()>compared_obj.DealOut()       ?  1  :  this.DealOut()<compared_obj.DealOut()        ?  -1 :  0);
                               case POS_SORT_MODE_ID            :  return(this.ID()>compared_obj.ID()                 ?  1  :  this.ID()<compared_obj.ID()                  ?  -1 :  0);
                               case POS_SORT_MODE_MAGIC         :  return(this.Magic()>compared_obj.Magic()           ?  1  :  this.Magic()<compared_obj.Magic()            ?  -1 :  0);
                               case POS_SORT_MODE_SYMBOL        :  return(this.Symbol()>compared_obj.Symbol()         ?  1  :  this.Symbol()<compared_obj.Symbol()          ?  -1 :  0);
                               case POS_SORT_MODE_PRICE_IN      :  return(this.PriceIn()>compared_obj.PriceIn()       ?  1  :  this.PriceIn()<compared_obj.PriceIn()        ?  -1 :  0);
                               case POS_SORT_MODE_PRICE_OUT     :  return(this.PriceOut()>compared_obj.PriceOut()     ?  1  :  this.PriceOut()<compared_obj.PriceOut()      ?  -1 :  0);
                               case POS_SORT_MODE_VOLUME        :  return(this.Volume()>compared_obj.Volume()         ?  1  :  this.Volume()<compared_obj.Volume()          ?  -1 :  0);
                               default                          :  return(this.TimeInMsc()>compared_obj.TimeInMsc()   ?  1  :  this.TimeInMsc()<compared_obj.TimeInMsc()    ?  -1 :  0);
                              }
                           }
    //--- Return a position type description
       string            TypeDescription(void) const
                           {
                            return(this.m_type==POSITION_TYPE_BUY ? "Buy" : this.m_type==POSITION_TYPE_SELL ? "Sell" : "Unknown");
                           }
    //--- Print the properties of the position and its deals in the journal
       void              Print(void)
                           {
                            //--- Display a header with a position description
                            ::PrintFormat
                              (
                               "Position %s %s #%lld, Magic %lld\n-Opened at %s at a price of %.*f\n-Closed at %s at a price of %.*f:",
                               this.TypeDescription(),this.Symbol(),this.ID(),this.Magic(),
                               this.TimeMSCtoString(this.TimeInMsc()), this.m_digits,this.PriceIn(),
                               this.TimeMSCtoString(this.TimeOutMsc()),this.m_digits,this.PriceOut()
                              );
                            //--- Display deal descriptions in a loop by all position deals
                            for(int i=0;i<this.m_list_deals.Total();i++)
                              {
                               CDeal *deal=this.m_list_deals.At(i);
                               if(deal==NULL)
                                  continue;
                               deal.Print();
                              }
                           }
    //--- Constructor
                         CPosition(const long position_id) : m_time_in(0), m_time_in_msc(0),m_time_out(0),m_time_out_msc(0),m_deal_in_ticket(0),m_deal_out_ticket(0),m_type(WRONG_VALUE)
                           {
                            this.m_list_deals.Sort(DEAL_SORT_MODE_TIME_MSC);
                            this.m_position_id=position_id;
                            this.m_account_currency=::AccountInfoString(ACCOUNT_CURRENCY);
                           }
      };
    
    

    privateセクションには、ポジションプロパティを格納するクラスメンバー変数が宣言され、補助メソッドも記述されます。

    BarOpenTime()メソッドは、渡された特定の時間に基づいて、指定された時間枠のバーの開始時間を計算して返します。

       datetime          BarOpenTime(const ENUM_TIMEFRAMES timeframe,const datetime time) const
                           {
                            ENUM_TIMEFRAMES period=(timeframe==PERIOD_CURRENT ? ::Period() : timeframe);
                            //--- Calculate the bar open time on periods less than W1
                            if(period<PERIOD_W1)
                               return time-time%::PeriodSeconds(period);
                            //--- Calculate the bar open time on W1
                            if(period==PERIOD_W1)
                               return time-(time+4*24*60*60)%::PeriodSeconds(period);
                            //--- Calculate the bar open time on MN1
                            else
                              {
                               MqlDateTime dt;
                               ::ResetLastError();
                               if(!::TimeToStruct(time,dt))
                                 {
                                  ::PrintFormat("%s: TimeToStruct failed. Error %lu",__FUNCTION__,::GetLastError());
                                  return 0;
                                 }
                               return time-(time%(24*60*60))-(dt.day-1)*(24*60*60);
                              }
                           }
    
    

    例えば、ある時間を知っていて、その時間が対応するチャート期間内にあるバーの開始時間を知りたいとします。このメソッドは、計算されたバーの開始時間を返します。こうすることで、あるチャートバーである時刻に、あるポジションが存在したかどうかを常に調べることができます。

    //--- Return the flag of the existence of a position at the specified time
       bool              IsPresentInTime(const datetime time) const
                           {
                            return(time>=this.BarTimeOpenPosition() && time<=this.BarTimeClosePosition());
                           }
    
    

    あるいは、単にポジションのオープンバーやクローズバーを取得することもできます。

    //--- Returns the start time of the bar (1) opening, (2) closing a position on the current chart period
       datetime          BarTimeOpenPosition(void) const
                           {
                            return this.BarOpenTime(PERIOD_CURRENT,this.TimeIn());
                           }
       datetime          BarTimeClosePosition(void) const
                           {
                            return this.BarOpenTime(PERIOD_CURRENT,this.TimeOut());
                           }
    
    

    現在のチャートバーのポジションの損益を計算するには、簡略化した近似計算原理を使用します。あるケースでは、ポジションの始値と現在のバーの終値に対する利益ポイントの数を考慮します。2つ目のケースでは、これらの損益ポイントのコストを計算します。

       double            ProfitRelativeClosePrice(const double price_close,const datetime time,const bool points) const
                           {
                            //--- If there was no position at the specified time, return 0
                            if(!this.IsPresentInTime(time))
                               return 0;
                            //--- Calculate the number of profit points depending on the position direction
                            int pp=int((this.TypePosition()==POSITION_TYPE_BUY ? price_close-this.PriceIn() : this.PriceIn()-price_close)/this.m_point);
                            //--- If the profit is in points, return the calculated number of points
                            if(points)
                               return pp;
                            //--- Otherwise, return the calculated number of points multiplied by (cost of one point * position volume)
                            return pp*this.GetOnePointPrice(time)*this.Volume();
                           }
    
    

    両手法とも、ポジション存続期間中の変動利益の完全な把握はできませんが、ここではその必要はありません。ここでは、閉じられたポジションのデータを取得する方法を示すことがより重要です。正確な計算のためには、ポジションの存続期間中の各バーにおける市場の状態について、より多くの異なるデータが必要になります。バーの開始時間から終了時間までのティックを取得し、この期間のポジションの状態をモデル化するためにそれらを使用する必要があります。それなら、それぞれのバーで同じことをすればいいのです。今のところ、そのような仕事はありません。

    オブジェクトが作成されるポジションのIDは、クラスのコンストラクタに渡されます。

                         CPosition(const long position_id) : m_time_in(0), m_time_in_msc(0),m_time_out(0),m_time_out_msc(0),m_deal_in_ticket(0),m_deal_out_ticket(0),m_type(WRONG_VALUE)
                           {
                            this.m_list_deals.Sort(DEAL_SORT_MODE_TIME_MSC);
                            this.m_position_id=position_id;
                            this.m_account_currency=::AccountInfoString(ACCOUNT_CURRENCY);
                           }
    
    

    IDは、クラスオブジェクトが作成された時点ですでに知られています。トランザクションプロパティから取得します。ポジションクラスのオブジェクトを作成した後、ポジションIDが取得された選択された取引を取引リストに追加します。これはすべて、過去ポジションリストクラスでおこなわれます。


    過去ポジションリストクラス

    //+------------------------------------------------------------------+
    //| Historical position list class                                   |
    //+------------------------------------------------------------------+
    class CHistoryPosition
      {
    private:
      CArrayObj          m_list_pos;          // Historical position list
    public:
    //--- Create historical position list
       bool              CreatePositionList(const string symbol=NULL);
    //--- Return a position object from the list by (1) index and (2) ID
       CPosition        *GetPositionObjByIndex(const int index)
                           {
                            return this.m_list_pos.At(index);
                           }
       CPosition        *GetPositionObjByID(const long id)
                           {
                            //--- Create a temporary position object
                            CPosition *tmp=new CPosition(id);
                            //--- Set the list of positions to be sorted by position ID
                            this.m_list_pos.Sort(POS_SORT_MODE_ID);
                            //--- Get the index of the object in the list of positions with this ID
                            int index=this.m_list_pos.Search(tmp);
                            delete tmp;
                            this.m_list_pos.Sort(POS_SORT_MODE_TIME_IN_MSC);
                            return this.m_list_pos.At(index);
                           }
    //--- Add a deal to the list of deals
       bool              DealAdd(const long position_id,CDeal *deal)
                           {
                            CPosition *pos=this.GetPositionObjByID(position_id);
                            return(pos!=NULL ? pos.DealAdd(deal) : false);
                           }
    //--- Return the flag of the location of the specified position at the specified time
       bool              IsPresentInTime(CPosition *pos,const datetime time) const
                           {
                            return pos.IsPresentInTime(time);
                           }
    //--- Return the position profit relative to the close price
       double            ProfitRelativeClosePrice(CPosition *pos,const double price_close,const datetime time,const bool points) const
                           {
                            return pos.ProfitRelativeClosePrice(price_close,time,points);
                           }
    //--- Return the number of historical positions
       int               PositionsTotal(void) const { return this.m_list_pos.Total();   }
    //--- Print the properties of positions and their deals in the journal
       void              Print(void)
                           {
                            for(int i=0;i<this.m_list_pos.Total();i++)
                              {
                               CPosition *pos=this.m_list_pos.At(i);
                               if(pos==NULL)
                                  continue;
                               pos.Print();
                              }
                           }
    //--- Constructor
                         CHistoryPosition(void)
                           {
                            this.m_list_pos.Sort(POS_SORT_MODE_TIME_IN_MSC);
                           }
      };
    
    

    クラスのprivateセクションで、過去ポジションのオブジェクトへのポインタを格納するリストを宣言します。publicメソッドは、ある程度はこのリストと連動するように設計されています。

    ではもっと詳しくみていきます。

    CreatePositionList()メソッドは、過去ポジションのリストを作成します。

    //+------------------------------------------------------------------+
    //| CHistoryPosition::Create historical position list                |
    //+------------------------------------------------------------------+
    bool CHistoryPosition::CreatePositionList(const string symbol=NULL)
      {
    //--- If failed to request the history of deals and orders, return 'false'
       if(!::HistorySelect(0,::TimeCurrent()))
          return false;
    //--- Declare a result variable and a pointer to the position object
       bool res=true;
       CPosition *pos=NULL;
    //--- In a loop based on the number of history deals
       int total=::HistoryDealsTotal();
       for(int i=0;i<total;i++)
         {
          //--- get the ticket of the next deal in the list
          ulong ticket=::HistoryDealGetTicket(i);
          //--- If the deal ticket has not been received, or this is a balance deal, or if the deal symbol is specified, but the deal has been performed on another symbol, move on 
          if(ticket==0 || ::HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_BALANCE || (symbol!=NULL && symbol!="" && ::HistoryDealGetString(ticket,DEAL_SYMBOL)!=symbol))
             continue;
          //--- Create a deal object and, if the object could not be created, add 'false' to the 'res' variable and move on
          CDeal *deal=new CDeal(ticket);
          if(deal==NULL)
            {
             res &=false;
             continue;
            }
          //--- Get the value of the position ID from the deal 
          long pos_id=deal.PositionID();
          //--- Get the pointer to a position object from the list
          pos=this.GetPositionObjByID(pos_id);
          //--- If such a position is not yet in the list,
          if(pos==NULL)
            {
             //--- create a new position object. If failed to do that, add 'false' to the 'res' variable, remove the deal object and move on
             pos=new CPosition(pos_id);
             if(pos==NULL)
               {
                res &=false;
                delete deal;
                continue;
               }
             //--- Set the sorting by time flag in milliseconds to the list of positions and add the position object to the corresponding place in the list
             this.m_list_pos.Sort(POS_SORT_MODE_TIME_IN_MSC);
             //--- If failed to add the position object to the list, add 'false' to the 'res' variable, remove the deal and position objects and move on
             if(!this.m_list_pos.InsertSort(pos))
               {
                res &=false;
                delete deal;
                delete pos;
                continue;
               }
            }
          //--- If a position object is created and added to the list
          //--- If the deal object could not be added to the list of deals of the position object, add 'false' to the 'res' variable, remove the deal object and move on
          if(!pos.DealAdd(deal))
            {
             res &=false;
             delete deal;
             continue;
            }
          //--- All is successful.
          //--- Set position properties depending on the deal type
          if(deal.Entry()==DEAL_ENTRY_IN)
            {
             pos.SetSymbol(deal.Symbol());
             pos.SetDealIn(deal.Ticket());
             pos.SetTimeIn(deal.Time());
             pos.SetTimeInMsc(deal.TimeMsc());
             ENUM_POSITION_TYPE type=ENUM_POSITION_TYPE(deal.TypeDeal()==DEAL_TYPE_BUY ? POSITION_TYPE_BUY : deal.TypeDeal()==DEAL_TYPE_SELL ? POSITION_TYPE_SELL : WRONG_VALUE);
             pos.SetType(type);
             pos.SetPriceIn(deal.Price());
             pos.SetVolume(deal.Volume());
            }
          if(deal.Entry()==DEAL_ENTRY_OUT || deal.Entry()==DEAL_ENTRY_OUT_BY)
            {
             pos.SetDealOut(deal.Ticket());
             pos.SetTimeOut(deal.Time());
             pos.SetTimeOutMsc(deal.TimeMsc());
             pos.SetPriceOut(deal.Price());
            }
          if(deal.Entry()==DEAL_ENTRY_INOUT)
            {
             ENUM_POSITION_TYPE type=ENUM_POSITION_TYPE(deal.TypeDeal()==DEAL_TYPE_BUY ? POSITION_TYPE_BUY : deal.TypeDeal()==DEAL_TYPE_SELL ? POSITION_TYPE_SELL : WRONG_VALUE);
             pos.SetType(type);
             pos.SetVolume(deal.Volume()-pos.Volume());
            }
         }
    //--- Return the result of creating and adding a position to the list
       return res;
      }
    
    

    端末の過去の取引リストをループして、次の取引を取得し、そこからポジションIDを取得し、そのIDを持つポジションオブジェクトが過去のポジションリストに存在するかどうかを確認します。リストにそのようなポジションがまだない場合は、新しいポジションオブジェクトを作成し、リストに追加します。そのようなポジションがすでに存在する場合は、リストからこのオブジェクトへのポインタを取得します。次に、その取引を過去ポジションの取引リストに追加します。次に、取引タイプに基づき、ポジションに固有で、取引プロパティに記録されているプロパティをポジションオブジェクトに入力します。

    このように、すべての過去の取引をループで調べながら、過去ポジションのリストを作成し、その中で各ポジションに属する取引をリストに格納します。取引リストに基づいてポジションリストを作成する際、一部決済されたポジションの出来高の変動は考慮されません。必要であれば、そのような機能を追加すべきです。なぜなら、ここでは過去ポジションのリストを作成する例を見ているのであって、ポジションが存続している間のポジションの特性の変化を正確に再現しているわけではないからです。

    色で示したコードブロックに、説明した機能を追加する必要があります。あるいは、OOPの原則に従えば、CreatePositionList()メソッドを仮想メソッドとし、過去ポジションのリストのクラスを継承して、独自のクラスでこのメソッドの実装をおこなうこともできます。あるいは、取引プロパティによるポジションプロパティの変更(色で示したコードのブロック)をprotected仮想メソッドに移し、CreatePositionList()を完全に書き直すことなく、継承したクラスでこのprotectedメソッドをオーバーライドすることもできます。

    GetPositionObjByIndex()メソッドは、渡されたインデックスによって、リストから過去ポジションオブジェクトへのポインタを返します。
    指定されたインデックスがリストにあるオブジェクトへのポインタを取得します。オブジェクトが見つからない場合はNULLを取得します。

    GetPositionObjByID()メソッドは、渡されたポジションIDによって、リストから過去のポジションオブジェクトへのポインタを返します。

       CPosition        *GetPositionObjByID(const long id)
                           {
                            //--- Create a temporary position object
                            CPosition *tmp=new CPosition(id);
                            //--- Set the list of positions to be sorted by position ID
                            this.m_list_pos.Sort(POS_SORT_MODE_ID);
                            //--- Get the index of the object in the list of positions with this ID
                            int index=this.m_list_pos.Search(tmp);
                            //--- Remove the temporary object and set the sorting by time flag for the list in milliseconds.
                            delete tmp;
                            this.m_list_pos.Sort(POS_SORT_MODE_TIME_IN_MSC);
                            //--- Return the pointer to the object in the list at the received index, or NULL if the index was not received 
                            return this.m_list_pos.At(index);
                           }
    
    

    ポジションIDがメソッドに渡されたものと等しいオブジェクトが、リストから検索されます。結果は、見つかったオブジェクトへのポインタであり、オブジェクトが見つからなければNULLです。

    DealAdd()メソッドは、取引を取引リストに追加します。

       bool              DealAdd(const long position_id,CDeal *deal)
                           {
                            CPosition *pos=this.GetPositionObjByID(position_id);
                            return(pos!=NULL ? pos.DealAdd(deal) : false);
                           }
    
    

    このメソッドは、取引が追加されるポジションのIDを受け取ります。取引へのポインタもメソッドに渡されます。取引が正常にポジションの取引リストに追加された場合、このメソッドはtrueを返し、そうでない場合はfalseを返します。

    IsPresentInTime()メソッドは、指定された時刻における指定されたポジションのフラグを返します。

       bool              IsPresentInTime(CPosition *pos,const datetime time) const
                           {
                            return pos.IsPresentInTime(time);
                           }
    
    

    このメソッドは、確認が必要なポジションへのポインタと、そのポジションを確認するのに必要な期間を受け取ります。指定された時刻にそのポジションが存在した場合、このメソッドはtrueを返し、そうでない場合はfalseを返します。

    ProfitRelativeClosePrice()メソッドは、終値に対するポジションの利益を返します。

       double            ProfitRelativeClosePrice(CPosition *pos,const double price_close,const datetime time,const bool points) const
                           {
                            return pos.ProfitRelativeClosePrice(price_close,time,points);
                           }
    
    

    このメソッドは、データを取得する必要があるポジションへのポインタ、ポジションの利益を取得する必要があるバーの終値とその時刻、およびデータ単位(ポイント数またはポイント数コスト)を示すフラグを受け取ります。

    PositionsTotal()メソッドは、リストに含まれる過去のポジション数を返します。

       int               PositionsTotal(void) const { return this.m_list_pos.Total();   }
    
    

    Print()メソッドは、操作ログにポジションのプロパティとその取引を出力します。

       void              Print(void)
                           {
                            //--- In the loop through the list of historical positions 
                            for(int i=0;i<this.m_list_pos.Total();i++)
                              {
                               //--- get the pointer to the position object and print the properties of the position and its deals
                               CPosition *pos=this.m_list_pos.At(i);
                               if(pos==NULL)
                                  continue;
                               pos.Print();
                              }
                           }
    
    

    クラスのコンストラクタで、過去のポジションリストにミリ秒単位の時間による並び替えフラグを設定します。

    //--- Constructor
                         CHistoryPosition(void)
                           {
                            this.m_list_pos.Sort(POS_SORT_MODE_TIME_IN_MSC);
                           }
    
    

    この計画を実行するためのすべてのクラスを作りました。

    では、口座に存在する現在の銘柄の全ポジションのリストを作成し、過去のデータの各バーにその損益のチャートを描く指標コードを書いてみましょう。


    ポジション利益グラフ指標

    指標バッファを図として表示するのに最適な描画スタイルは、2つの指標バッファの間の色付き領域、DRAW_FILLINGです。

    DRAW_FILLINGスタイルは、2つの指標バッファの値の間に色の付いた領域をプロットします。実際、このスタイルは2本の線を引き、その間を指定された2色のうちの1色で塗りつぶす。チャンネルを描画する指標を作成するために使用されます。どのバッファにもnull値のみを含めることはできません。この場合、何もプロットされないからです。

    2つの塗りつぶし色を設定できます。

    • 1番目の色は、1番目のバッファの値が2番目の指標バッファの値よりも大きい領域に使用されます。
    • 2番目の色は、2番目のバッファの値が1番目の指標バッファの値よりも大きい領域に使用されます。

    塗りつぶしの色は、コンパイラのディレクティブを使用するか、PlotIndexSetInteger()関数を使用して動的に設定することができます。プロットプロパティのダイナミックな変更により、指標を「活気づける」ことができ、現在の状況に応じて指標の外観が変化します。

    指標は、両方の指標バッファの値が0でも空値でもないすべてのバーに対して計算されます。どの値を「空」とみなすかを指定するには、PLOT_EMPTY_VALUEプロパティにこの値を設定します。

       #define INDICATOR_EMPTY_VALUE -1.0
       ...
    //--- the INDICATOR_EMPTY_VALUE (empty) value will not be used in the calculation
       PlotIndexSetDouble(plot_index_DRAW_FILLING,PLOT_EMPTY_VALUE,INDICATOR_EMPTY_VALUE);
    

    指標の計算に参加しないバーの描画は、指標バッファの値に依存します。

    • 両方の指標バッファの値が0に等しいバーは、指標の描画に関与しません。これは、値がゼロの領域が埋まっていないことを意味します。


    • 指標バッファの値が「空の値」に等しいバーは、指標の描画に関与します。空の値の領域は、重要な値を持つ領域をつなぐように塗りつぶされます。


    「空の値」がゼロに等しい場合、指標の計算に関与しないバーも塗りつぶされることに注意すべきです。

    DRAW_FILLINGのプロットに必要なバッファの数は2です。

    バーのポジション利益測定単位を選択するための入力を1つ追加します。過去ポジションリストクラスのインスタンスへのポインタを宣言し、OnInit()ハンドラを少し補足します。

    //+------------------------------------------------------------------+
    //| Indicator                                                        |
    //+------------------------------------------------------------------+
    //--- input parameters
    input bool     InpProfitPoints   =  true;    // Profit in Points
    //--- indicator buffers
    double         BufferFilling1[];
    double         BufferFilling2[];
    //--- global variables
    CHistoryPosition *history=NULL;
    //+------------------------------------------------------------------+
    //| Custom indicator initialization function                         |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- indicator buffers mapping
       SetIndexBuffer(0,BufferFilling1,INDICATOR_DATA);
       SetIndexBuffer(1,BufferFilling2,INDICATOR_DATA);
    //--- Set the indexing of buffer arrays as in a timeseries
       ArraySetAsSeries(BufferFilling1,true);
       ArraySetAsSeries(BufferFilling2,true);
    //--- Set Digits of the indicator data equal to 2 and one level equal to 0
       IndicatorSetInteger(INDICATOR_DIGITS,2);
       IndicatorSetInteger(INDICATOR_LEVELS,1);
       IndicatorSetDouble(INDICATOR_LEVELVALUE,0);
    //--- Create a new historical data object
       history=new CHistoryPosition();
    //--- Return the result of creating a historical data object
       return(history!=NULL ? INIT_SUCCEEDED : INIT_FAILED);
      }
    
    

    OnDeinit()ハンドラを書いて、作成した過去ポジションリストクラスのインスタンスを削除し、チャートコメントを削除します。

    //+------------------------------------------------------------------+
    //| Custom indicator deinitialization function                       |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- Delete the object of historical positions and chart comments
       if(history!=NULL)
          delete history;
       Comment("");
      }
    
    

    OnCalculate()ハンドラで、つまり最初のティックで、すべての過去ポジションのリストを作成し、メインループで、指標ループの現在のバーで利益を計算する関数を使用して、作成したリストにアクセスします。

    //+------------------------------------------------------------------+
    //| Custom indicator iteration function                              |
    //+------------------------------------------------------------------+
    int OnCalculate(const int rates_total,
                    const int prev_calculated,
                    const datetime &time[],
                    const double &open[],
                    const double &high[],
                    const double &low[],
                    const double &close[],
                    const long &tick_volume[],
                    const long &volume[],
                    const int &spread[])
      {
    //--- Set 'close' and 'time' array indexing as in timeseries
       ArraySetAsSeries(close,true);
       ArraySetAsSeries(time,true);
    //--- Flag of the position list successful creation
       static bool done=false;
    //--- If the position data object is created
       if(history!=NULL)
         {
          //--- If no position list was created yet
          if(!done)
            {
             //--- If the list of positions for the current symbol has been successfully created,
             if(history.CreatePositionList(Symbol()))
               {
                //--- print positions in the journal and set the flag for successful creation of a list of positions 
                history.Print();
                done=true;
               }
            }
         }
    //--- Number of bars required to calculate the indicator
       int limit=rates_total-prev_calculated;
    //--- If 'limit' exceeds 1, this means this is the first launch or a change in historical data
       if(limit>1)
         {
          //--- Set the number of bars for calculation equal to the entire available history and initialize the buffers with "empty" values 
          limit=rates_total-1;
          ArrayInitialize(BufferFilling1,EMPTY_VALUE);
          ArrayInitialize(BufferFilling2,EMPTY_VALUE);
         }
    //--- In the loop by symbol history bars
       for(int i=limit;i>=0;i--)
         {
          //--- get the profit of positions present on the bar with the i loop index and write the resulting value to the first buffer
          double profit=Profit(close[i],time[i]);
          BufferFilling1[i]=profit;
          //--- Always write zero to the second buffer. Depending on whether the value in the first buffer is greater or less than zero,
          //--- the fill color between arrays 1 and 2 of the indicator buffer will change
          BufferFilling2[i]=0;
         }
    //--- return value of prev_calculated for the next call
       return(rates_total);
      }
    
    

    ここでは、過去のデータの始まりから現在の日付まで、通常の指標のループが配置されています。ループの中で、各バーについて、ループの現在のバーの過去ポジションの利益を計算する関数を呼び出します。

    //+-----------------------------------------------------------------------+
    //| Return the profit of all positions from the list at the specified time|
    //+-----------------------------------------------------------------------+
    double Profit(const double price,const datetime time)
      {
    //--- Variable for recording and returning the result of calculating profit on a bar
       double res=0;
    //--- In the loop by the list of historical positions
       for(int i=0;i<history.PositionsTotal();i++)
         {
          //--- get the pointer to the next position 
          CPosition *pos=history.GetPositionObjByIndex(i);
          if(pos==NULL)
             continue;
          //--- add the value of calculating the profit of the current position, relative to the 'price' on the bar with 'time', to the result
          res+=pos.ProfitRelativeClosePrice(price,time,InpProfitPoints);
         }
    //--- Return the calculated amount of profit of all positions relative to the 'price' on the bar with 'time'
       return res;
      }
    
    

    この関数は、ポジションの利益ポイント数を取得する相対価格(バーの終了)と、ポジションの存在を確認する時間(バーの開始時間)を取得します。次に、過去ポジションの各オブジェクトから受け取ったすべてのポジションの利益を合計して返します。

    コンパイル後、多くの未決済ポジションがある銘柄のチャート上で指標を実行すると、過去のすべてのポジションの利益チャートが描画されます。



    結論

    取引からポジションを復元し、口座に存在するすべての過去ポジションのリストを作成する可能性を検討しました。この例は単純なもので、取引から正確にポジションを復元するためのすべての側面を網羅しているわけではありませんが、ポジション復元に必要な精度を独自に補うには十分です。


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

    添付されたファイル |
    知っておくべきMQL5ウィザードのテクニック(第10回):型破りなRBM 知っておくべきMQL5ウィザードのテクニック(第10回):型破りなRBM
    制限ボルツマンマシン(Restrictive Boltzmann Machine、RBM)は、基本的なレベルでは、次元削減による教師なし分類に長けた2層のニューラルネットワークです。その基本原理を採用し、常識にとらわれない方法で設計し直して訓練すれば有用なシグナルフィルタが得られるかどうかを検証します。
    MQL5における拡張ディッキー–フラー検定の実装 MQL5における拡張ディッキー–フラー検定の実装
    本稿では、拡張ディッキー–フラー検定の実装を示し、Engle-Granger法を用いた共和分検定に適用します。
    知っておくべきMQL5ウィザードのテクニック(第11回):ナンバーウォール 知っておくべきMQL5ウィザードのテクニック(第11回):ナンバーウォール
    ナンバーウォールは、リニアシフトバックレジスタの一種で、収束を確認することにより、予測可能な数列を事前にスクリーニングします。これらのアイデアがMQL5でどのように役立つかを見ていきます。
    float16およびfloat8形式のONNXモデルを扱う float16およびfloat8形式のONNXモデルを扱う
    機械学習モデルの表現に使用されるデータ形式は、その有効性に決定的な役割を果たします。近年、深層学習モデルを扱うために特別に設計された新しい型のデータがいくつか登場しています。この記事では、現代のモデルで広く採用されるようになった2つの新しいデータ形式に焦点を当てます。