過去のポジションをチャート上に損益図として表示する
内容
はじめに
ポジションとは、サーバーに送られた取引注文を実行した結果です。つまり、注文→取引→ポジションです。
ポジションが開いている銘柄名を示しながら、ネットポジション勘定の場合、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つのクラスが必要です。
- 取引クラス:ポジションとそのプロパティを識別するために必要な取引プロパティを含む
- ポジションクラス:ポジションに関与した取引のリストと、ポジション固有のプロパティを含む
- 過去ポジションリストクラス:検索された過去ポジションのリストで、指定されたプロパティに基づいてポジションを選択できる
これら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
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索