
取引履歴を気にせずにチャート上で直接取引を表示する方法
内容
はじめに
現代のトレーダーは、取引操作に関連する膨大なデータを分析するという課題にしばしば直面します。MetaTrader 5クライアント端末では、チャートがオープンポジションとクローズポジションのラベルで埋め尽くされ、取引履歴の表示が非効率的でわかりにくくなることがあります。特に、大量の取引を扱うトレーダーにとっては、チャートがすぐに一杯になり、取引活動を分析して情報に基づいた意思決定をおこなうことがほとんど不可能になるケースもあります。
この記事の目的は、取引履歴の認識と分析を容易にするソリューションを提供することです。クローズされたポジションと改善された取引情報を段階的に表示するメカニズムを開発し、トレーダーが個々の取引に集中し、取引操作をより深く理解できるようにすることを目指します。
以下を実装します。
- ナビゲーションキーを使用してクローズされたポジションを1つずつ表示する機能
- 改良された、各取引に関する詳細情報を提供するツールチップ
- 最も重要な要素が常に表示されるようにグラフを中央に配置する機能
これらを実装するために必要なデータ構造体を詳細に検討し、MetaTrader 5での取引とポジション計算の基本原則を提案します。これにより、トレーダーは取引履歴をより効果的に管理し、得られた情報を基に情報に基づいた意思決定をおこなえるようになります。
MetaTrader 5クライアント端末では、チャート設定(F8キー)の[表示]タブで[取引履歴を表示]オプションをオンにすることで、取引履歴を表示できます。
このオプションをオンにすると、チャート上に、線で結ばれたポジションの開始および終了のラベル形式で取引履歴全体が表示されるようになります。この際、銘柄ごとに実行されたすべての取引が完全に表示されます。
取引が多数ある場合、チャートはラベルで乱雑になり、チャート上の情報が見えにくくなります。また、取引ラベルやそれを結ぶ線の上にマウスを合わせた際に表示されるツールチップも、特に有益な情報を提供しているとは言えません。
ここで紹介するソリューションは、起動時に最後にクローズされたポジションのみを表示します。残りのポジションへの移動は、カーソルキーを押して実行されます。
- 上矢印キーを押すと、最初にクローズされたポジションが表示される
- 下矢印キーを押すと、最後にクローズされたポジションが表示される
- 左矢印キーを押すと、前にクローズされたポジションが表示される
- 右矢印キーを押すと、次にクローズされたポジションが表示される
取引ラベルと接続線のツールチップは、より有用な情報を表示するよう改善されています。また、Shiftキーを押したままにすることで、現在選択されているクローズポジションに関する情報がチャートに表示されるように設計されています。
後続の各クローズポジションを表示する際は、ポジションの開始/終了ラベルとそれらを結ぶ線がチャートの中央に配置されるようにします。
開始ラベルと終了ラベルの両方が1つのチャート画面内に収まらない場合は、開始取引がチャートの左から2番目の表示バーに配置されるよう調整します。
次に、取引とポジションの計算の基本原則、ならびにここで作成するクラスの構造を確認します。
ポジションの形成過程は次の通りです。まず、取引リクエスト(注文)がサーバーに送信されます。この注文はキャンセルされるか実行されます。実行されると取引(ディール)が発生し、新たなポジションが生成されます。ポジションがない状態で取引が実行されると、取引によってポジションが新規に作成されます。一方、ポジションが既に存在する場合、その計算方式に応じて複数のオプションがあります。ネッティング計算の場合:1つの銘柄につき1つのポジションのみが可能です。したがって、新しい取引で既存ポジションが以下のように変更されます。
- クローズ:ロングポジションに対して、ポジション取引量と等しい量の売りが実行された場合
買いポジション(1.0) - 売り取引(1.0) = 取引量0(ポジションのクローズ) - 部分クローズ:ロングポジションに対して、ポジション取引量より少ない量の売りが実行された場合
買いポジション(1.0) - 売り取引(0.5) = 取引量0.5(ポジションの部分クローズ) - 取引量追加:ロングポジションに対して、新たな買いが実行された場合
買いポジション(1.0) + 買い取引(0.5) = 取引量1.5(ポジション取引量の増加) - 反転:ロングポジションに対してポジション取引量よりも大きい量の売りが実行された場合
買いポジション(1.0) - 売り取引(1.5) = 売りポジション(0.5) (ポジションの反転)
ヘッジ口座タイプの場合、各取引が既存のポジションを変更するか、新規ポジションを生成します。既存ポジションを変更する(クローズまたは部分クローズ)場合、取引にポジションIDを指定する必要があります。ポジションIDは、各新しいポジションに割り当てられる一意の番号であり、ポジションの存続期間中は変更されません。
- 新しいポジションを建てる:別のポジションがまだ存在する間に買いまたは売りが実行された場合
買いポジション(1.0) + 売り取引(1.0) = 独立した買いポジション(1.0)と売りポジション(1.0)(ポジションを建てる)、または
買いポジション(1.0) + 買い取引(1.5) = 独立した買いポジション(1.0)と買いポジション(1.5)(ポジションを建てる)など。 - 既存ポジションのクローズ:既存のロングポジションのIDを指定し、それに対する売りが実行された場合
買いポジション(1.0)(ID123) - 売り取引(1.0)(ID123) = ポジションクローズ(1.0)(ID123)(ID123ポジションのクローズ) - 既存ポジションの部分クローズ:指定されたIDの既存ポジションのボリュームよりも少ないボリュームで、既存のロングポジションのIDを使用して売りが実行された場合
買いポジション(1.0)(ID123) - 売り取引(0.5)(ID123)= 取引量0.5(ID123)(ID123ポジション部分クローズ)
注文、取引、ポジションの詳細については「MetaTrader 5の注文、ポジション、取引」をご覧ください。
各注文は、取引サーバーに送信されると、実行されるまで端末内のアクティブ注文リストに残ります。注文がトリガーされると、取引が発生し、新しいポジションを生成するか、既存のポジションを変更します。注文が実行される瞬間、その注文は履歴注文のリストに移動され、同時に取引が生成されます。この取引は履歴取引リストに登録されます。取引がポジションを生成する場合、新しいポジションが作成され、アクティブポジションのリストに配置されます。
端末には、過去の(クローズされた)ポジションのリストは存在しません。また、アクティブな取引のリストもありません。それらは実行されるとすぐに履歴として扱われ、対応するリストに配置されます。
その理由は簡単です。注文とは、口座で取引操作を実行するための指示です。注文は履行されるまで存在し、既存の注文リストに表示されます。注文が実行されると(理想的には)存在しなくなり、実行済み注文のリストに移動されます。このプロセスで取引が生成されます。取引とは、取引注文が実行されたという事実を指します。これは一度限りのイベントであり、注文が実行されると取引が発生します。それだけのことです。取引は、完了した口座イベントのリストに追加されます。
取引はポジションを生成し、そのポジションはクローズされるまで存在します。ポジションは常にアクティブポジションのリストに表示され、クローズされるとそのリストから削除されます。
端末にはクローズされたポジションのリストはありませんが、完了した取引のリストは存在します。したがって、クローズされたポジションのリストを作成するには、以前にクローズされたポジションに属していた取引からそれらを再構成する必要があります。取引リストは常に利用可能であり、各取引には関連するポジションのIDが含まれています。このため、以前口座上に存在していた各ポジションのライフヒストリーを再現するために必要な情報(ポジションIDとその取引のリスト)にすべてアクセス可能です。
上記に基づき、以下のステップに従って、既存の取引履歴リストからクローズポジションを再作成できます。
- 必要な銘柄のすべての取引のリストを取得する
- リストを確認して、リストから次の取引を取得する
- 取引プロパティでポジションIDを確認する
- 独自の履歴ポジションリストにそのようなIDのポジションが存在しない場合は、履歴ポジションオブジェクトを新規作成する
- 以前受信したIDを使用して履歴ポジションオブジェクトを取得する
- 現在の取引を履歴ポジションオブジェクト取引のリストに追加する
端末取引のリストのループが完了すると、各ポジションに属する取引のリストを含む、銘柄別の口座上のすべてのクローズポジションの完全なリストが得られます。
これで、各履歴ポジションには、そのポジションの変更に関係する取引のリストが表示され、ポジションが存在する間にクローズされたポジションのすべての変更に関する完全な情報を取得できるようになります。
上記のロジックを実装するには、次の3つのクラスを作成する必要があります。
- 取引クラス:このクラスには、過去の取引のすべてのプロパティのリスト、プロパティにアクセスするためのメソッド(設定と取得)、および取引データを表示するための追加メソッドが含まれます。
- ポジションクラス:このクラスには、すべての取引のリスト、それらにアクセスするためのメソッド、およびポジションとその取引に関する情報を表示するための追加メソッドが含まれます。
- 履歴ポジション管理クラス:このクラスには、すべての履歴ポジションのリストが含まれ、それを作成および更新するほか、ポジションとその取引のプロパティにアクセスするためのメソッド、さらにポジションとその取引に関する情報を表示するための追加メソッドも含まれます。
始めましょう。
取引クラス
取引のプロパティを取得して保存するには、注文および取引の履歴を選択する必要があります(HistorySelect())。取引履歴をループで調べ、履歴取引のリストからループインデックスを使って取引チケットを取得します(HistoryDealGetTicket())。この場合、取引はHistoryDealGetInteger()、HistoryDealGetDouble()、HistoryDealGetString()関数を使用してプロパティを取得するために選択されます。
取引クラスでは、取引がすでに選択されており、そのプロパティをすぐに取得できることを前提としています。取引プロパティの記録と取得に加えて、クラスを使用すると、取引を表示するチャートにグラフィカルラベルを作成できます。ラベルはキャンバスに描画されるため、定義済みのフォントシンボルセットのラベルを使用する代わりに、各タイプの取引に必要なラベルを描画できます。各取引のオブジェクトを作成する際には、取引がおこなわれた瞬間の価格のティック履歴を取得し、取引時に存在したスプレッドを計算して、取引パラメータの説明に表示することができます。
各ポジションには、その変更に参加した多くの取引が存在する可能性があるため、すべての取引は、最終的にCObjectクラスとそのCArrayObjの子孫のインスタンスへのポインタの動的配列のクラスに配置されます。
したがって、取引クラスは、標準ライブラリのCObject基底オブジェクトクラスから継承されます。
取引には、整数、実数、文字列のプロパティのセットがあります。各取引オブジェクトは、履歴ポジションクラスのオブジェクトのリストにあります。必要な取引の検索を実装するには、パラメータの列挙を記述する必要があります。この列挙の値を使用して、リスト内の取引を検索し、同じ値でリストを並べ替えることができます。検索は、取引チケットとミリ秒単位の時間というプロパティのみでおこないます。チケットを使用すると、そのような取引がリストにすでに存在するかどうかを判断できます。ミリ秒単位の時間を使用すると、取引を厳密に時系列順に並べることができます。ただし、クラスは拡張を意味するため、取引プロパティのリストにはそのすべてのプロパティが含まれます。
標準ライブラリのCArrayObjリストで検索と並べ替えを整理するために、CObjectクラスでは、値0を返すCompare()仮想メソッドが提供されます。
//--- method of comparing the objects virtual int Compare(const CObject *node,const int mode=0) const { return(0); }
つまり、このメソッドは常に等価性を返します。CObjectから継承されたクラスの各オブジェクトでは、このメソッドを再定義して、比較対象の2つのオブジェクトのプロパティ(modeパラメータで指定)を比較するときに、メソッドが次を返すようにする必要があります。
- -1:現在のオブジェクトプロパティの値が比較対象オブジェクトの値よりも小さい場合
- 1:現在のオブジェクトプロパティの値が比較対象オブジェクトの値よりも大きい場合
- 0:比較する両方のオブジェクトで指定されたプロパティの値が等しい場合
したがって、上記の説明に基づいて、今日作成された各クラスに対して、クラスオブジェクトのプロパティの列挙とそれらを比較するためのメソッドをさらに作成する必要があります。
\MQL5\Include\フォルダ内にPositionsViewer\フォルダを作成して、CDealクラスの新しいファイルDeal.mqhを含めます。
標準ライブラリのCObjectクラスが基底クラスである必要があります。
その結果、取引オブジェクトクラスファイルの次のテンプレートが得られます。
//+------------------------------------------------------------------+ //| Deal.mqh | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" class CDeal : public CObject { private: public: CDeal(); ~CDeal(); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CDeal::CDeal() { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CDeal::~CDeal() { } //+------------------------------------------------------------------+
CObjectライブラリの標準オブジェクトクラスとCCanvasクラスのファイルを、取引オブジェクトクラスの作成されたファイルにインクルードして、取引オブジェクトのプロパティで並べ替えるための列挙を記述します。
//+------------------------------------------------------------------+ //| Deal.mqh | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include <Object.mqh> #include <Canvas\Canvas.mqh> enum ENUM_DEAL_SORT_MODE { SORT_MODE_DEAL_TICKET = 0, // Mode of comparing/sorting by a deal ticket SORT_MODE_DEAL_ORDER, // Mode of comparing/sorting by the order a deal is based on SORT_MODE_DEAL_TIME, // Mode of comparing/sorting by a deal time SORT_MODE_DEAL_TIME_MSC, // Mode of comparing/sorting by a deal time in milliseconds SORT_MODE_DEAL_TYPE, // Mode of comparing/sorting by a deal type SORT_MODE_DEAL_ENTRY, // Mode of comparing/sorting by a deal direction SORT_MODE_DEAL_MAGIC, // Mode of comparing/sorting by a deal magic number SORT_MODE_DEAL_REASON, // Mode of comparing/sorting by a deal reason or source SORT_MODE_DEAL_POSITION_ID, // Mode of comparing/sorting by a position ID SORT_MODE_DEAL_VOLUME, // Mode of comparing/sorting by a deal volume SORT_MODE_DEAL_PRICE, // Mode of comparing/sorting by a deal price SORT_MODE_DEAL_COMMISSION, // Mode of comparing/sorting by commission SORT_MODE_DEAL_SWAP, // Mode of comparing/sorting by accumulated swap on close SORT_MODE_DEAL_PROFIT, // Mode of comparing/sorting by a deal financial result SORT_MODE_DEAL_FEE, // Mode of comparing/sorting by a deal fee SORT_MODE_DEAL_SL, // Mode of comparing/sorting by Stop Loss level SORT_MODE_DEAL_TP, // Mode of comparing/sorting by Take Profit level SORT_MODE_DEAL_SYMBOL, // Mode of comparing/sorting by a name of a traded symbol SORT_MODE_DEAL_COMMENT, // Mode of comparing/sorting by a deal comment SORT_MODE_DEAL_EXTERNAL_ID, // Mode of comparing/sorting by a deal ID in an external trading system }; //+------------------------------------------------------------------+ //| Deal class | //+------------------------------------------------------------------+ class CDeal : public CObject { }
クラスが動作するために必要なすべての変数とメソッドをprivate、protected、publicセクションで宣言します。
//+------------------------------------------------------------------+ //| Deal class | //+------------------------------------------------------------------+ class CDeal : public CObject { private: MqlTick m_tick; // Deal tick structure //--- CCanvas object CCanvas m_canvas; // Canvas long m_chart_id; // Chart ID int m_width; // Canvas width int m_height; // Canvas height string m_name; // Graphical object name //--- Create a label object on the chart bool CreateLabelObj(void); //--- Draw (1) a mask, (2) Buy arrow on the canvas void DrawArrowMaskBuy(const int shift_x, const int shift_y); void DrawArrowBuy(const int shift_x, const int shift_y); //--- Draw (1) a mask, (2) Sell arrow on the canvas void DrawArrowMaskSell(const int shift_x, const int shift_y); void DrawArrowSell(const int shift_x, const int shift_y); //--- Draw the label appearance void DrawLabelView(void); //--- Get a (1) deal tick and (2) a spread of the deal minute bar bool GetDealTick(const int amount=20); int GetSpreadM1(void); //--- Return time with milliseconds string TimeMscToString(const long time_msc,int flags=TIME_DATE|TIME_MINUTES|TIME_SECONDS) const; protected: //--- Integer properties long m_ticket; // Deal ticket. Unique number assigned to each deal long m_order; // Deal order number datetime m_time; // Deal execution time long m_time_msc; // Deal execution time in milliseconds since 01.01.1970 ENUM_DEAL_TYPE m_type; // Deal type ENUM_DEAL_ENTRY m_entry; // Deal entry - entry in, entry out, reverse long m_magic; // Magic number for a deal (see ORDER_MAGIC) ENUM_DEAL_REASON m_reason; // Deal execution reason or source long m_position_id; // The ID of the position opened, modified or closed by the deal //--- Real properties double m_volume; // Deal volume double m_price; // Deal price double m_commission; // Deal commission double m_swap; // Accumulated swap when closing double m_profit; // Deal financial result double m_fee; // Fee for making a deal charged immediately after performing a deal double m_sl; // Stop Loss level double m_tp; // Take Profit level //--- String properties string m_symbol; // Name of the symbol for which the deal is executed string m_comment; // Deal comment string m_external_id; // Deal ID in an external trading system (on the exchange) //--- Additional properties int m_digits; // Symbol digits double m_point; // Symbol point double m_bid; // Bid when performing a deal double m_ask; // Ask when performing a deal int m_spread; // Spread when performing a deal color m_color_arrow; // Deal label color //--- Draws an arrow corresponding to the deal type. It can be redefined in the inherited classes virtual void DrawArrow(void); public: //--- Set the properties //--- Integer properties void SetTicket(const long ticket) { this.m_ticket=ticket; } // Ticket void SetOrder(const long order) { this.m_order=order; } // Order void SetTime(const datetime time) { this.m_time=time; } // Time void SetTimeMsc(const long value) { this.m_time_msc=value; } // Time in milliseconds void SetTypeDeal(const ENUM_DEAL_TYPE type) { this.m_type=type; } // Type void SetEntry(const ENUM_DEAL_ENTRY entry) { this.m_entry=entry; } // Direction void SetMagic(const long magic) { this.m_magic=magic; } // Magic number void SetReason(const ENUM_DEAL_REASON reason) { this.m_reason=reason; } // Deal execution reason or source void SetPositionID(const long id) { this.m_position_id=id; } // Position ID //--- Real properties void SetVolume(const double volume) { this.m_volume=volume; } // Volume void SetPrice(const double price) { this.m_price=price; } // Price void SetCommission(const double value) { this.m_commission=value; } // Commission void SetSwap(const double value) { this.m_swap=value; } // Accumulated swap when closing void SetProfit(const double value) { this.m_profit=value; } // Financial result void SetFee(const double value) { this.m_fee=value; } // Deal fee void SetSL(const double value) { this.m_sl=value; } // Stop Loss level void SetTP(const double value) { this.m_tp=value; } // Take Profit level //--- String properties void SetSymbol(const string symbol) { this.m_symbol=symbol; } // Symbol name void SetComment(const string comment) { this.m_comment=comment; } // Comment void SetExternalID(const string ext_id) { this.m_external_id=ext_id; } // Deal ID in an external trading system //--- Get the properties //--- Integer properties long Ticket(void) const { return(this.m_ticket); } // Ticket long Order(void) const { return(this.m_order); } // Order datetime Time(void) const { return(this.m_time); } // Time long TimeMsc(void) const { return(this.m_time_msc); } // Time in milliseconds ENUM_DEAL_TYPE TypeDeal(void) const { return(this.m_type); } // Type ENUM_DEAL_ENTRY Entry(void) const { return(this.m_entry); } // Direction long Magic(void) const { return(this.m_magic); } // Magic number ENUM_DEAL_REASON Reason(void) const { return(this.m_reason); } // Deal execution reason or source long PositionID(void) const { return(this.m_position_id); } // Position ID //--- Real properties double Volume(void) const { return(this.m_volume); } // Volume double Price(void) const { return(this.m_price); } // Price double Commission(void) const { return(this.m_commission); } // Commission double Swap(void) const { return(this.m_swap); } // Accumulated swap when closing double Profit(void) const { return(this.m_profit); } // Financial result double Fee(void) const { return(this.m_fee); } // Deal fee double SL(void) const { return(this.m_sl); } // Stop Loss level double TP(void) const { return(this.m_tp); } // Take Profit level double Bid(void) const { return(this.m_bid); } // Bid when performing a deal double Ask(void) const { return(this.m_ask); } // Ask when performing a deal int Spread(void) const { return(this.m_spread); } // Spread when performing a deal //--- String properties string Symbol(void) const { return(this.m_symbol); } // Symbol name string Comment(void) const { return(this.m_comment); } // Comment string ExternalID(void) const { return(this.m_external_id); } // Deal ID in an external trading system //--- Set the color of the deal label void SetColorArrow(const color clr); //--- (1) Hide, (2) display the deal label on a chart void HideArrow(const bool chart_redraw=false); void ShowArrow(const bool chart_redraw=false); //--- Return the description of a (1) deal type, (2) position change method and (3) deal reason string TypeDescription(void) const; string EntryDescription(void) const; string ReasonDescription(void) const; //--- Return (1) a short description and (2) a tooltip text of a deal string Description(void); virtual string Tooltip(void); //--- Print deal properties in the journal void Print(void); //--- Compare two objects by the property specified in 'mode' virtual int Compare(const CObject *node, const int mode=0) const; //--- Constructors/destructor CDeal(void) { this.m_ticket=0; } CDeal(const ulong ticket); ~CDeal(); };
宣言されたメソッドについて詳しく考えてみましょう。
以下は、パラメトリックコンストラクタです。
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CDeal::CDeal(const ulong ticket) { //--- Store the properties //--- Integer properties this.m_ticket = (long)ticket; // Deal ticket this.m_order = ::HistoryDealGetInteger(ticket, DEAL_ORDER); // Order this.m_time = (datetime)::HistoryDealGetInteger(ticket, DEAL_TIME); // Deal execution time this.m_time_msc = ::HistoryDealGetInteger(ticket, DEAL_TIME_MSC); // Deal execution time in milliseconds this.m_type = (ENUM_DEAL_TYPE)::HistoryDealGetInteger(ticket, DEAL_TYPE); // Type this.m_entry = (ENUM_DEAL_ENTRY)::HistoryDealGetInteger(ticket, DEAL_ENTRY); // Direction this.m_magic = ::HistoryDealGetInteger(ticket, DEAL_MAGIC); // Magic number this.m_reason = (ENUM_DEAL_REASON)::HistoryDealGetInteger(ticket, DEAL_REASON); // Deal execution reason or source this.m_position_id= ::HistoryDealGetInteger(ticket, DEAL_POSITION_ID); // Position ID //--- Real properties this.m_volume = ::HistoryDealGetDouble(ticket, DEAL_VOLUME); // Volume this.m_price = ::HistoryDealGetDouble(ticket, DEAL_PRICE); // Price this.m_commission = ::HistoryDealGetDouble(ticket, DEAL_COMMISSION); // Commission this.m_swap = ::HistoryDealGetDouble(ticket, DEAL_SWAP); // Accumulated swap when closing this.m_profit = ::HistoryDealGetDouble(ticket, DEAL_PROFIT); // Financial result this.m_fee = ::HistoryDealGetDouble(ticket, DEAL_FEE); // Deal fee this.m_sl = ::HistoryDealGetDouble(ticket, DEAL_SL); // Stop Loss level this.m_tp = ::HistoryDealGetDouble(ticket, DEAL_TP); // Take Profit level //--- String properties this.m_symbol = ::HistoryDealGetString(ticket, DEAL_SYMBOL); // Symbol name this.m_comment = ::HistoryDealGetString(ticket, DEAL_COMMENT); // Comment this.m_external_id= ::HistoryDealGetString(ticket, DEAL_EXTERNAL_ID); // Deal ID in an external trading system //--- Graphics display parameters this.m_chart_id = ::ChartID(); this.m_digits = (int)::SymbolInfoInteger(this.m_symbol, SYMBOL_DIGITS); this.m_point = ::SymbolInfoDouble(this.m_symbol, SYMBOL_POINT); this.m_width = 19; this.m_height = 19; this.m_name = "Deal#"+(string)this.m_ticket; this.m_color_arrow= (this.TypeDeal()==DEAL_TYPE_BUY ? C'3,95,172' : this.TypeDeal()==DEAL_TYPE_SELL ? C'225,68,29' : C'180,180,180'); //--- Parameters for calculating spread this.m_spread = 0; this.m_bid = 0; this.m_ask = 0; //--- Create a graphic label this.CreateLabelObj(); //--- If the historical tick and the Point value of the symbol were obtained if(this.GetDealTick() && this.m_point!=0) { //--- set the Bid and Ask price values, calculate and save the spread value this.m_bid=this.m_tick.bid; this.m_ask=this.m_tick.ask; this.m_spread=int((this.m_ask-this.m_bid)/this.m_point); } //--- If failed to obtain a historical tick, take the spread value of the minute bar the deal took place on else this.m_spread=this.GetSpreadM1(); }
現在の取引はすでに選択されていることに注意してください。したがって、ここでは、取引プロパティからすべてのオブジェクトプロパティをすぐに入力します。次に、チャートに取引ラベルを表示するためのキャンバスパラメータを設定し、このラベルのグラフィカルオブジェクトを作成し、取引時間によるティックを取得して、取引時に存在するスプレッドを計算します。ティックを取得できなかった場合は、取引がおこなわれた分足の平均スプレッドを取得します。
その結果、取引オブジェクトを作成すると、チケットに応じて設定された履歴取引のプロパティを持つオブジェクトと、チャート上で取引を視覚化するための作成済みの非表示のラベル、および取引時のスプレッド値がすぐに取得されます。
クラスデストラクタで、初期化解除の理由を確認します。これがチャートの時間枠の変更ではない場合は、グラフィカルラベルオブジェクトのグラフィカルリソースを破棄します。
//+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CDeal::~CDeal() { if(::UninitializeReason()!=REASON_CHARTCHANGE) this.m_canvas.Destroy(); }
以下は、指定されたプロパティによって2つのオブジェクトを相互に比較する仮想メソッドです。
//+------------------------------------------------------------------+ //| Compare two objects by the specified property | //+------------------------------------------------------------------+ int CDeal::Compare(const CObject *node,const int mode=0) const { const CDeal * obj = node; switch(mode) { case SORT_MODE_DEAL_TICKET : return(this.Ticket() > obj.Ticket() ? 1 : this.Ticket() < obj.Ticket() ? -1 : 0); case SORT_MODE_DEAL_ORDER : return(this.Order() > obj.Order() ? 1 : this.Order() < obj.Order() ? -1 : 0); case SORT_MODE_DEAL_TIME : return(this.Time() > obj.Time() ? 1 : this.Time() < obj.Time() ? -1 : 0); case SORT_MODE_DEAL_TIME_MSC : return(this.TimeMsc() > obj.TimeMsc() ? 1 : this.TimeMsc() < obj.TimeMsc() ? -1 : 0); case SORT_MODE_DEAL_TYPE : return(this.TypeDeal() > obj.TypeDeal() ? 1 : this.TypeDeal() < obj.TypeDeal() ? -1 : 0); case SORT_MODE_DEAL_ENTRY : return(this.Entry() > obj.Entry() ? 1 : this.Entry() < obj.Entry() ? -1 : 0); case SORT_MODE_DEAL_MAGIC : return(this.Magic() > obj.Magic() ? 1 : this.Magic() < obj.Magic() ? -1 : 0); case SORT_MODE_DEAL_REASON : return(this.Reason() > obj.Reason() ? 1 : this.Reason() < obj.Reason() ? -1 : 0); case SORT_MODE_DEAL_POSITION_ID : return(this.PositionID() > obj.PositionID() ? 1 : this.PositionID() < obj.PositionID() ? -1 : 0); case SORT_MODE_DEAL_VOLUME : return(this.Volume() > obj.Volume() ? 1 : this.Volume() < obj.Volume() ? -1 : 0); case SORT_MODE_DEAL_PRICE : return(this.Price() > obj.Price() ? 1 : this.Price() < obj.Price() ? -1 : 0); case SORT_MODE_DEAL_COMMISSION : return(this.Commission() > obj.Commission() ? 1 : this.Commission() < obj.Commission() ? -1 : 0); case SORT_MODE_DEAL_SWAP : return(this.Swap() > obj.Swap() ? 1 : this.Swap() < obj.Swap() ? -1 : 0); case SORT_MODE_DEAL_PROFIT : return(this.Profit() > obj.Profit() ? 1 : this.Profit() < obj.Profit() ? -1 : 0); case SORT_MODE_DEAL_FEE : return(this.Fee() > obj.Fee() ? 1 : this.Fee() < obj.Fee() ? -1 : 0); case SORT_MODE_DEAL_SL : return(this.SL() > obj.SL() ? 1 : this.SL() < obj.SL() ? -1 : 0); case SORT_MODE_DEAL_TP : return(this.TP() > obj.TP() ? 1 : this.TP() < obj.TP() ? -1 : 0); case SORT_MODE_DEAL_SYMBOL : return(this.Symbol() > obj.Symbol() ? 1 : this.Symbol() < obj.Symbol() ? -1 : 0); case SORT_MODE_DEAL_COMMENT : return(this.Comment() > obj.Comment() ? 1 : this.Comment() < obj.Comment() ? -1 : 0); case SORT_MODE_DEAL_EXTERNAL_ID : return(this.ExternalID() > obj.ExternalID() ? 1 : this.ExternalID() < obj.ExternalID() ? -1 : 0); default : return(-1); } }
このメソッドは、比較対象オブジェクトへのポインタとENUM_DEAL_SORT_MODE列挙体からの比較対象プロパティの値を受け取ります。現在のオブジェクトの指定されたプロパティの値が比較対象オブジェクトのプロパティの値より大きい場合は1、小さい場合は-1、それ以外の場合は0が返されます。
以下は、取引の種類の説明を返すメソッドです。
//+------------------------------------------------------------------+ //| Return the deal type description | //+------------------------------------------------------------------+ string CDeal::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 position change method | //+------------------------------------------------------------------+ string CDeal::EntryDescription(void) const { switch(this.m_entry) { case DEAL_ENTRY_IN : return "Entry In"; case DEAL_ENTRY_OUT : return "Entry Out"; case DEAL_ENTRY_INOUT : return "Reverse"; case DEAL_ENTRY_OUT_BY : return "Close a position by an opposite one"; default : return "Unknown: "+(string)this.m_entry; } }
以下は、取引理由の説明を返すメソッドです。
//+------------------------------------------------------------------+ //| Return a deal reason description | //+------------------------------------------------------------------+ string CDeal::ReasonDescription(void) const { switch(this.m_reason) { case DEAL_REASON_CLIENT : return "Terminal"; case DEAL_REASON_MOBILE : return "Mobile"; case DEAL_REASON_WEB : return "Web"; case DEAL_REASON_EXPERT : return "EA"; case DEAL_REASON_SL : return "SL"; case DEAL_REASON_TP : return "TP"; case DEAL_REASON_SO : return "SO"; case DEAL_REASON_ROLLOVER : return "Rollover"; case DEAL_REASON_VMARGIN : return "Var. Margin"; case DEAL_REASON_SPLIT : return "Split"; case DEAL_REASON_CORPORATE_ACTION: return "Corp. Action"; default : return "Unknown reason "+(string)this.m_reason; } }
このメソッドでは、StopLoss、TakeProfit、StopOutによるクローズの3つの値のみを使用します。
以下は、取引の説明を返すメソッドです。
//+------------------------------------------------------------------+ //| Return deal description | //+------------------------------------------------------------------+ string CDeal::Description(void) { return(::StringFormat("Deal: %-9s %.2f %-4s #%I64d at %s", this.EntryDescription(), this.Volume(), this.TypeDescription(), this.Ticket(), this.TimeMscToString(this.TimeMsc()))); }
StringFormat()関数を使用して、次の文字列を作成して返します。
Deal: Entry In 0.10 Buy #1728374638 at 2023.06.12 16:51:36.838
以下は、取引ポップアップメッセージのテキストを返す仮想メソッドです。
//+------------------------------------------------------------------+ //| Returns a text of a deal pop-up message | //+------------------------------------------------------------------+ string CDeal::Tooltip(void) { return(::StringFormat("Position ID #%I64d %s:\nDeal #%I64d %.2f %s %s\n%s [%.*f]\nProfit: %.2f, SL: %.*f, TP: %.*f", this.PositionID(), this.Symbol(), this.Ticket(), this.Volume(), this.TypeDescription(), this.EntryDescription(), this.TimeMscToString(this.TimeMsc()), this.m_digits, this.Price(), this.Profit(), this.m_digits, this.SL(), this.m_digits, this.TP())); }
上で説明したメソッドと同様に、ここでは次のタイプのテキスト文字列が形成され、返されます。
Position ID #1752955040 EURUSD: Deal #1728430603 0.10 Sell Entry Out 2023.06.12 17:04:20.362 [1.07590] Profit: 15.00, SL: 1.07290, TP: 1.07590
この文字列は、後でチャート上の取引ラベルの上にマウスポインタを置いたときに、ツールチップに取引の説明を表示するために使用されます。このメソッドは継承されたクラスで再定義して、他の取引情報を出力することができます。
以下は、操作ログに取引プロパティを出力するメソッドです。
//+------------------------------------------------------------------+ //| Print deal properties in the journal | //+------------------------------------------------------------------+ void CDeal::Print(void) { ::PrintFormat(" %s", this.Description()); }
このメソッドは、すべての取引プロパティを出力するのではなく、上で説明したDescription()メソッドによって返された取引に関する最小限の情報のみを表示します。ポジションの説明を表示するときに情報を整理するために、取引の説明文字列の前に2つのスペースが追加されます。ポジションの説明ヘッダーの後にポジション取引のデータが続きます。
Position EURUSD 0.10 Buy #1752955040, Magic 0 -Opened 2023.06.12 16:51:36.838 [1.07440] -Closed 2023.06.12 17:04:20.362 [1.07590] Deal: Entry In 0.10 Buy #1728374638 at 2023.06.12 16:51:36.838 Deal: Entry Out 0.10 Sell #1728430603 at 2023.06.12 17:04:20.362
PrintFormat()関数とStringFormat()関数の詳細については、「PrintFormat()を調べてすぐ使える例を適用する」および「StringFormat():レビューと既成の例」をご覧ください。
以下は、ミリ秒単位で時間を返すメソッドです。
//+------------------------------------------------------------------+ //| Return time with milliseconds | //+------------------------------------------------------------------+ string CDeal::TimeMscToString(const long time_msc, int flags=TIME_DATE|TIME_MINUTES|TIME_SECONDS) const { return(::TimeToString(time_msc/1000, flags) + "." + ::IntegerToString(time_msc %1000, 3, '0')); }
ここでは、1000で割って通常の時間に変換されたミリ秒単位の時間値を含む文字列を形成します。ミリ秒単位の時間を1000で割った余りとして得られたミリ秒数が、結果の文字列に点の後に追加されます。ミリ秒の文字列は3桁の数値としてフォーマットされ、3桁より短い場合は先頭に0が追加されます。その結果、時間の表現は次のようになります。
2023.06.12 17:04:20.362
取引時のスプレッドがどのようなものであったかを理解するためには、取引時間に必要なティックをミリ秒単位で取得する必要があります。標準のCopyTicks()関数を使用して既知の時間に1つのティックをコピーするという一見些細なタスクは、指定された正確な時間にティックをコピーすることができなかったため、解決策を見つけるための小さな探求につながりました。結果として、解決策を少し探した後、必要なアルゴリズムを見つけることができました。取引時間までの「開始」から拡大し続ける時間範囲で、一定数のティックリクエストをおこなう必要があります。詳細はこちら(ロシア語)をご覧ください。
以下は、取引のティックを取得するメソッドです。
//+------------------------------------------------------------------+ //| Get the deal tick | //+------------------------------------------------------------------+ bool CDeal::GetDealTick(const int amount=20) { MqlTick ticks[]; // We will receive ticks here int attempts = amount; // Number of attempts to get ticks int offset = 500; // Initial time offset for an attempt int copied = 0; // Number of ticks copied //--- Until the tick is copied and the number of copy attempts is over //--- we try to get a tick, doubling the initial time offset at each iteration (expand the "from_msc" time range) while(!::IsStopped() && (copied<=0) && (attempts--)!=0) copied = ::CopyTicksRange(this.m_symbol, ticks, COPY_TICKS_INFO, this.m_time_msc-(offset <<=1), this.m_time_msc); //--- If the tick was successfully copied (it is the last one in the tick array), set it to the m_tick variable if(copied>0) this.m_tick=ticks[copied-1]; //--- Return the flag that the tick was copied return(copied>0); }
ティックを受け取った後、そこからAskおよびBid価格が取得され、スプレッドサイズは(Ask - Bid) / ポイントとして計算されます。
このメソッドを使用してティックを取得できなかった場合は、取引分足のスプレッドを取得する方法を使用してスプレッドの平均値を取得します。
//+------------------------------------------------------------------+ //| Gets the spread of the deal minute bar | //+------------------------------------------------------------------+ int CDeal::GetSpreadM1(void) { int array[1]={}; int bar=::iBarShift(this.m_symbol, PERIOD_M1, this.Time()); if(bar==WRONG_VALUE) return 0; return(::CopySpread(this.m_symbol, PERIOD_M1, bar, 1, array)==1 ? array[0] : 0); }
ここでは、取引時間によって分足バーの開始時間を取得します。これは、CopySpread()関数を使用してバーの平均スプレッドを取得するために使用されます。バーまたはスプレッドの取得中にエラーが発生した場合、メソッドは0を返します。
以下は、チャート上にラベルオブジェクトを作成するメソッドです。
//+------------------------------------------------------------------+ //| Create a label object on the chart | //+------------------------------------------------------------------+ bool CDeal::CreateLabelObj(void) { //--- Create a graphical resource with a Bitmap object attached to it ::ResetLastError(); if(!this.m_canvas.CreateBitmap(this.m_name, this.m_time, this.m_price, this.m_width, this.m_height, COLOR_FORMAT_ARGB_NORMALIZE)) { ::PrintFormat("%s: When creating a graphic object, error %d occurred in the CreateBitmap method of the CCanvas class",__FUNCTION__, ::GetLastError()); return false; } //--- If the graphical resource is successfully created, set the Bitmap object, anchor point, price, time and tooltip text ::ObjectSetInteger(this.m_chart_id, this.m_name, OBJPROP_ANCHOR, ANCHOR_CENTER); ::ObjectSetInteger(this.m_chart_id, this.m_name, OBJPROP_TIME, this.Time()); ::ObjectSetDouble(this.m_chart_id, this.m_name, OBJPROP_PRICE, this.Price()); ::ObjectSetString(this.m_chart_id, this.m_name, OBJPROP_TOOLTIP, this.Tooltip()); //--- Hide the created object from the chart and draw its appearance on it this.HideArrow(); this.DrawLabelView(); return true; }
グラフィカルリソースを作成し、そのアンカーポイントを中央に設定し、取引価格と時間、およびツールチップテキストを設定します。次に、作成されたオブジェクトに、すべてのチャート期間で非表示であることを示すフラグが設定され、その外観が描画されます。これを表示するには、すべての時間枠でオブジェクトの可視性フラグを設定する必要があります。
以下は、ラベルオブジェクトの外観を描画するメソッドです。
//+------------------------------------------------------------------+ //| Draw the appearance of the label object | //+------------------------------------------------------------------+ void CDeal::DrawLabelView(void) { this.m_canvas.Erase(0x00FFFFFF); this.DrawArrow(); this.m_canvas.Update(true); }
まず、ビットマップグラフィカルオブジェクトが完全に透明な色で塗りつぶされ、次に取引の種類に対応する矢印がその上に描画され、その後、チャートが再描画されるときにキャンバスが更新されます。
以下は、取引の種類に対応する矢印を描画する仮想メソッドです。
//+------------------------------------------------------------------+ //| Draw an arrow corresponding to the deal type | //+------------------------------------------------------------------+ void CDeal::DrawArrow(void) { switch(this.TypeDeal()) { case DEAL_TYPE_BUY : this.DrawArrowBuy(5, 10); break; case DEAL_TYPE_SELL : this.DrawArrowSell(5, 0); break; default : break; } }
取引の種類(買いまたは売り)に応じて、対応する矢印を描画するメソッドが呼び出されます。このメソッドは仮想なので、継承されたクラスで再定義できます。例えば、子クラスでは、異なる種類の取引に対して異なるラベルを描画したり、ポジションの変更方法などを追加で考慮したりすることができます。
以下は、キャンバスに買い矢印マスクを描画するメソッドです。
//+------------------------------------------------------------------+ //| Draw Buy arrow mask on the canvas | //+------------------------------------------------------------------+ void CDeal::DrawArrowMaskBuy(const int shift_x, const int shift_y) { int x[]={4+shift_x, 8+shift_x, 8+shift_x, 6+shift_x, 6+shift_x, 2+shift_x, 2+shift_x, 0+shift_x, 0+shift_x, 4+shift_x}; int y[]={0+shift_y, 4+shift_y, 5+shift_y, 5+shift_y, 7+shift_y, 7+shift_y, 5+shift_y, 5+shift_y, 4+shift_y, 0+shift_y}; this.m_canvas.Polygon(x, y, ::ColorToARGB(clrWhite, 220)); }
取引ラベルとして描画された矢印には、ローソク足の暗い背景に対して目立つように、周囲に白いアウトライン(マスク)が付いています。
キャンバスに描画される図形の座標は常にキャンバスのローカル座標を基準として指定されるため、描画される図形をキャンバス内で正確に中央に配置するには、X軸とY軸に沿った破線ポイントの座標に追加されるオフセットを入力する必要があります。次に、メソッドに渡されたオフセットを考慮して、X座標とY座標の値が配列に設定され、Polygon()メソッドが呼び出されて、座標点を使用して白いアウトラインが描画されます。次に、矢印描画方法を使用して、アウトラインの内側に矢印を描画します。
以下は、キャンバス上に買い矢印を描画するメソッドです。
//+------------------------------------------------------------------+ //| Draw Buy arrow on the canvas | //+------------------------------------------------------------------+ void CDeal::DrawArrowBuy(const int shift_x, const int shift_y) { this.DrawArrowMaskBuy(shift_x, shift_y); int x[]={4+shift_x, 7+shift_x, 5+shift_x, 5+shift_x, 3+shift_x, 3+shift_x, 1+shift_x, 4+shift_x}; int y[]={1+shift_y, 4+shift_y, 4+shift_y, 6+shift_y, 6+shift_y, 4+shift_y, 4+shift_y, 1+shift_y}; this.m_canvas.Polygon(x, y, ::ColorToARGB(this.m_color_arrow)); this.m_canvas.Fill(4+shift_x, 4+shift_y, ::ColorToARGB(this.m_color_arrow)); }
ここでは、最初に買い矢印マスクを描画し、次に指定された座標を使用して買い矢印を描画し、その内部空間を色で塗りつぶします。
以下は、同様に売り矢印を描くメソッドです。
//+------------------------------------------------------------------+ //| Draw Sell arrow mask on the canvas | //+------------------------------------------------------------------+ void CDeal::DrawArrowMaskSell(const int shift_x, const int shift_y) { int x[]={4+shift_x, 0+shift_x, 0+shift_x, 2+shift_x, 2+shift_x, 6+shift_x, 6+shift_x, 8+shift_x, 8+shift_x, 4+shift_x}; int y[]={8+shift_y, 4+shift_y, 3+shift_y, 3+shift_y, 1+shift_y, 1+shift_y, 3+shift_y, 3+shift_y, 4+shift_y, 8+shift_y}; this.m_canvas.Polygon(x, y, ::ColorToARGB(clrWhite, 220)); } //+------------------------------------------------------------------+ //| Draw Sell arrow on the canvas | //+------------------------------------------------------------------+ void CDeal::DrawArrowSell(const int shift_x, const int shift_y) { this.DrawArrowMaskSell(shift_x, shift_y); int x[]={4+shift_x, 1+shift_x, 3+shift_x, 3+shift_x, 5+shift_x, 5+shift_x, 7+shift_x, 4+shift_x}; int y[]={7+shift_y, 4+shift_y, 4+shift_y, 2+shift_y, 2+shift_y, 4+shift_y, 4+shift_y, 7+shift_y}; this.m_canvas.Polygon(x, y, ::ColorToARGB(this.m_color_arrow)); this.m_canvas.Fill(4+shift_x, 4+shift_y, ::ColorToARGB(this.m_color_arrow)); }
以下は、チャート上で取引ラベルを非表示および表示するメソッドです。
//+------------------------------------------------------------------+ //| Hide the deal label on the chart | //+------------------------------------------------------------------+ void CDeal::HideArrow(const bool chart_redraw=false) { ::ObjectSetInteger(this.m_chart_id, this.m_name, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS); if(chart_redraw) ::ChartRedraw(this.m_chart_id); } //+------------------------------------------------------------------+ //| Display the deal label on the chart | //+------------------------------------------------------------------+ void CDeal::ShowArrow(const bool chart_redraw=false) { ::ObjectSetInteger(this.m_chart_id, this.m_name, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS); if(chart_redraw) ::ChartRedraw(this.m_chart_id); }
チャート上のオブジェクトを非表示にするには、グラフィカルオブジェクトのOBJPROP_TIMEFRAMES可視性プロパティはOBJ_NO_PERIODSに設定する必要があります。
したがって、オブジェクトを表示するには、OBJPROP_TIMEFRAMESプロパティをOBJ_ALL_PERIODSに設定する必要があります。
以下は、取引ラベルの色を設定するメソッドです。
//+------------------------------------------------------------------+ //| Set the deal label color | //+------------------------------------------------------------------+ void CDeal::SetColorArrow(const color clr) { this.m_color_arrow=clr; this.DrawLabelView(); }
描画されたラベルの色を格納するm_color_arrow変数はメソッドに渡された値に設定され、グラフィカルオブジェクト全体が完全に再描画されます。したがって、チャート上の取引ラベルの色を制御プログラムから「オンザフライ」で変更することが可能です。
チケットによる過去の取引のプロパティへのアクセスを提供し、この取引に関する必要なデータを取得してチャート上でラベルを表示または非表示にできる取引クラスを作成しました。
クラスオブジェクトはポジションオブジェクト取引のリストに保存され、履歴ポジションのプロパティが表示され、取引とパラメータへのアクセスが提供され、線で接続された開始取引と終了取引が表示されます。
ポジションクラス
取引クラスが作成されたのと同じフォルダに、CPositionクラスのファイルPosition.mqhを新規作成します。
取引クラスと同様に、オブジェクトプロパティの列挙を実装して、ポジションプロパティで検索および並べ替え、取引クラスファイルとポジションクラスのCArrayObjを接続します。
//+------------------------------------------------------------------+ //| Position.mqh | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include "Deal.mqh" #include <Arrays\ArrayObj.mqh> enum ENUM_POSITION_SORT_MODE { SORT_MODE_POSITION_TICKET = 0, // Mode of comparing/sorting by a position ticket SORT_MODE_POSITION_TIME, // Mode of comparing/sorting by position open time SORT_MODE_POSITION_TIME_MSC, // Mode of comparing/sorting by position open time im milliseconds SORT_MODE_POSITION_TIME_UPDATE, // Mode of comparing/sorting by position update time SORT_MODE_POSITION_TIME_UPDATE_MSC, // Mode of comparing/sorting by position update time im milliseconds SORT_MODE_POSITION_TYPE, // Mode of comparing/sorting by position type SORT_MODE_POSITION_MAGIC, // Mode of comparing/sorting by a position magic number SORT_MODE_POSITION_IDENTIFIER, // Mode of comparing/sorting by a position ID SORT_MODE_POSITION_REASON, // Mode of comparing/sorting by position open reason SORT_MODE_POSITION_VOLUME, // Mode of comparing/sorting by a position volume SORT_MODE_POSITION_PRICE_OPEN, // Mode of comparing/sorting by a position price SORT_MODE_POSITION_SL, // Mode of comparing/sorting by Stop Loss level for an open position SORT_MODE_POSITION_TP, // Mode of comparing/sorting by Take Profit level for an open position SORT_MODE_POSITION_PRICE_CURRENT, // Mode of comparing/sorting by the current symbol price SORT_MODE_POSITION_SWAP, // Mode of comparing/sorting by accumulated swap SORT_MODE_POSITION_PROFIT, // Mode of comparing/sorting by the current profit SORT_MODE_POSITION_SYMBOL, // Mode of comparing/sorting by a symbol a position is opened on SORT_MODE_POSITION_COMMENT, // Mode of comparing/sorting by a position comment SORT_MODE_POSITION_EXTERNAL_ID, // Mode of comparing/sorting by a position ID in an external system SORT_MODE_POSITION_TIME_CLOSE, // Mode of comparing/sorting by a position open time SORT_MODE_POSITION_TIME_CLOSE_MSC, // Mode of comparing/sorting by a position open time in milliseconds SORT_MODE_POSITION_PRICE_CLOSE, // Mode of comparing/sorting by a position price }; //+------------------------------------------------------------------+ //| Position class | //+------------------------------------------------------------------+ class CPosition : public CObject { }
クラスのprotectedセクションとpublicセクションに、クラスを処理するための変数とメソッドを入力します。
//+------------------------------------------------------------------+ //| Position class | //+------------------------------------------------------------------+ class CPosition : public CObject { private: protected: CArrayObj m_list_deals; // List of position deals CDeal m_temp_deal; // Temporary deal object for searching by property in the list //--- Return time with milliseconds string TimeMscToString(const long time_msc,int flags=TIME_DATE|TIME_MINUTES|TIME_SECONDS) const; //--- Integer properties long m_ticket; // Position ticket datetime m_time; // Position opening time long m_time_msc; // Position opening time in milliseconds since 01.01.1970 datetime m_time_update; // Position update time long m_time_update_msc; // Position update time in milliseconds since 01.01.1970 ENUM_POSITION_TYPE m_type; // Position type long m_magic; // Magic number for a position (see ORDER_MAGIC) long m_identifier; // Position ID ENUM_POSITION_REASON m_reason; // Position opening reason //--- Real properties double m_volume; // Position volume double m_price_open; // Position price double m_sl; // Stop Loss level for an open position double m_tp; // Take Profit level for an open position double m_price_current; // Current price by symbol double m_swap; // Accumulated swap double m_profit; // Current profit //--- String properties string m_symbol; // A symbol the position is open for string m_comment; // Position comment string m_external_id; // Position ID in an external system (on the exchange) //--- Additional properties long m_chart_id; // Chart ID int m_profit_pt; // Profit in points 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 string m_line_name; // Line graphical object name color m_line_color; // Connecting line color //--- Create a line connecting open-close deals virtual bool CreateLine(void); //--- Return the pointer to (1) open and (2) close deal CDeal *GetDealIn(void) const; CDeal *GetDealOut(void) const; //--- (1) Hide and (2) display deal labels on the chart void HideDeals(const bool chart_redraw=false); void ShowDeals(const bool chart_redraw=false); //--- (1) Hide and (2) display the connecting line between the deal labels void HideLine(const bool chart_redraw=false); void ShowLine(const bool chart_redraw=false); public: //--- Set the properties //--- Integer properties void SetTicket(const long ticket) { this.m_ticket=ticket; } // Position ticket void SetTime(const datetime time) { this.m_time=time; } // Position open time void SetTimeMsc(const long value) { this.m_time_msc=value; } // Position open time in milliseconds 01.01.1970 void SetTimeUpdate(const datetime time) { this.m_time_update=time; } // Position update time void SetTimeUpdateMsc(const long value) { this.m_time_update_msc=value; } // Position update time in milliseconds 01.01.1970 void SetTypePosition(const ENUM_POSITION_TYPE type) { this.m_type=type; } // Position type void SetMagic(const long magic) { this.m_magic=magic; } // Magic number for a position (see ORDER_MAGIC) void SetID(const long id) { this.m_identifier=id; } // Position ID void SetReason(const ENUM_POSITION_REASON reason) { this.m_reason=reason; } // Position opening reason //--- Real properties void SetVolume(const double volume) { this.m_volume=volume; } // Position volume void SetPriceOpen(const double price) { this.m_price_open=price; } // Position price void SetSL(const double value) { this.m_sl=value; } // Stop Loss level for an open position void SetTP(const double value) { this.m_tp=value; } // Take Profit level for an open position void SetPriceCurrent(const double price) { this.m_price_current=price; } // Current price by symbol void SetSwap(const double value) { this.m_swap=value; } // Accumulated swap void SetProfit(const double value) { this.m_profit=value; } // Current profit //--- String properties void SetSymbol(const string symbol) { this.m_symbol=symbol; } // Symbol a position is opened for void SetComment(const string comment) { this.m_comment=comment; } // Position comment void SetExternalID(const string ext_id) { this.m_external_id=ext_id; } // Position ID in an external system (on the exchange) //--- Get the properties //--- Integer properties long Ticket(void) const { return(this.m_ticket); } // Position ticket datetime Time(void) const { return(this.m_time); } // Position open time long TimeMsc(void) const { return(this.m_time_msc); } // Position open time in milliseconds since 01.01.1970 datetime TimeUpdate(void) const { return(this.m_time_update); } // Position update time long TimeUpdateMsc(void) const { return(this.m_time_update_msc);} // Position update time in milliseconds since 01.01.1970 ENUM_POSITION_TYPE TypePosition(void) const { return(this.m_type); } // Position type long Magic(void) const { return(this.m_magic); } // Magic number for a position (see ORDER_MAGIC) long ID(void) const { return(this.m_identifier); } // Position ID ENUM_POSITION_REASON Reason(void) const { return(this.m_reason); } // Position opening reason //--- Real properties double Volume(void) const { return(this.m_volume); } // Position volume double PriceOpen(void) const { return(this.m_price_open); } // Position price double SL(void) const { return(this.m_sl); } // Stop Loss level for an open position double TP(void) const { return(this.m_tp); } // Take Profit for an open position double PriceCurrent(void) const { return(this.m_price_current); } // Current price by symbol double Swap(void) const { return(this.m_swap); } // Accumulated swap double Profit(void) const { return(this.m_profit); } // Current profit //--- String properties string Symbol(void) const { return(this.m_symbol); } // A symbol position is opened on string Comment(void) const { return(this.m_comment); } // Position comment string ExternalID(void) const { return(this.m_external_id); } // Position ID in an external system (on the exchange) //--- Additional properties ulong DealIn(void) const; // Open deal ticket ulong DealOut(void) const; // Close deal ticket datetime TimeClose(void) const; // Close time long TimeCloseMsc(void) const; // Close time in milliseconds int ProfitInPoints(void) const; // Profit in points double PriceClose(void) const; // Close price //--- Add a deal to the list of deals, return the pointer CDeal *DealAdd(const long ticket); //--- Set the color of the (1) connecting line, (2) Buy and Sell deals void SetLineColor(const color clr=C'225,68,29'); void SetDealsColor(const color clr_deal_buy=C'3,95,172', const color clr_deal_sell=C'225,68,29'); //--- Return a position type description string TypeDescription(void) const; //--- Return position open time and price description string TimePriceCloseDescription(void); //--- Return position close time and price description string TimePriceOpenDescription(void); //--- Return a brief position description string Description(void); //--- Returns a text of a position popup message virtual string Tooltip(void); //--- Print the properties of the position and its deals in the journal void Print(void); //--- (1) Hide and (2) display a graphical representation of a position on a chart void Hide(const bool chart_redraw=false); void Show(const bool chart_redraw=false); //--- Compare two objects by the property specified in 'mode' virtual int Compare(const CObject *node, const int mode=0) const; //--- Constructor/destructor CPosition(const long position_id, const string symbol); CPosition(void) { this.m_symbol=::Symbol(); } ~CPosition(); };
CObjectクラスとその子孫CArrayObjのインスタンスへのポインタの動的配列のクラス内の並び替えられたリストを検索するには、Search()メソッドがオブジェクトのインスタンスへのポインタを受け取り、リストの並び替えに使用されるプロパティによる比較を実行する必要があります。特定のプロパティを持つオブジェクトを常に新規作成して破棄することはパフォーマンスの面で非常にコストがかかるため、取引オブジェクトのインスタンスはクラスのprotectedセクションで宣言されます。検索を実行するには、このオブジェクトに必要なプロパティの必要な値を設定し、それを目的のインスタンスとして検索メソッドに渡すだけです。
宣言されたメソッドを詳しく見てみましょう。
以下は、パラメトリックコンストラクタです。
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CPosition::CPosition(const long position_id, const string symbol) { this.m_list_deals.Sort(SORT_MODE_DEAL_TIME_MSC); this.m_identifier = position_id; this.m_account_currency = ::AccountInfoString(ACCOUNT_CURRENCY); this.m_symbol = (symbol==NULL ? ::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); this.m_chart_id = ::ChartID(); this.m_line_name = "line#"+(string)this.m_identifier; this.m_line_color = C'225,68,29'; }
コンストラクタは、ポジションIDとポジションが開かれた銘柄を受け取ります。ポジション取引のリストは、取引時間(ミリ秒単位)で並び替えるフラグを受け取ります。IDと銘柄はクラス変数に保存され、口座と銘柄のいくつかのパラメータ、チャートID、ポジションの開始と終了の取引ラベルを接続するラインのプロパティが設定されます。
クラスデストラクタで、プレフィックスがライン名であるすべてのグラフィカルオブジェクトを削除し、取引のリストをクリアします。
//+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CPosition::~CPosition() { ::ObjectDelete(this.m_chart_id, this.m_line_name); this.m_list_deals.Clear(); }
グラフィカルラインオブジェクトの削除は、次の理由によりプレフィックスによっておこなわれます。継承されたクラスが、開始と終了のラインだけでなく、すべてのポジション取引を接続する複数のラインを表示する場合、各ラインの名前を「line_name」+「line_number」として設定できます。この場合、すべてのラインは共通のプレフィックス「line_name」を持つため、デストラクタ内で一括して削除されます。
以下は、指定されたプロパティで2つのオブジェクトを比較するメソッドです。
//+------------------------------------------------------------------+ //| Compare two objects by the specified property | //+------------------------------------------------------------------+ int CPosition::Compare(const CObject *node,const int mode=0) const { const CPosition *obj=node; switch(mode) { case SORT_MODE_POSITION_TICKET : return(this.Ticket() > obj.Ticket() ? 1 : this.Ticket() < obj.Ticket() ? -1 : 0); case SORT_MODE_POSITION_TIME : return(this.Time() > obj.Time() ? 1 : this.Time() < obj.Time() ? -1 : 0); case SORT_MODE_POSITION_TIME_MSC : return(this.TimeMsc() > obj.TimeMsc() ? 1 : this.TimeMsc() < obj.TimeMsc() ? -1 : 0); case SORT_MODE_POSITION_TIME_UPDATE : return(this.TimeUpdate() > obj.TimeUpdate() ? 1 : this.TimeUpdate() < obj.TimeUpdate() ? -1 : 0); case SORT_MODE_POSITION_TIME_UPDATE_MSC: return(this.TimeUpdateMsc() > obj.TimeUpdateMsc() ? 1 : this.TimeUpdateMsc() < obj.TimeUpdateMsc() ? -1 : 0); case SORT_MODE_POSITION_TYPE : return(this.TypePosition() > obj.TypePosition() ? 1 : this.TypePosition() < obj.TypePosition() ? -1 : 0); case SORT_MODE_POSITION_MAGIC : return(this.Magic() > obj.Magic() ? 1 : this.Magic() < obj.Magic() ? -1 : 0); case SORT_MODE_POSITION_IDENTIFIER : return(this.ID() > obj.ID() ? 1 : this.ID() < obj.ID() ? -1 : 0); case SORT_MODE_POSITION_REASON : return(this.Reason() > obj.Reason() ? 1 : this.Reason() < obj.Reason() ? -1 : 0); case SORT_MODE_POSITION_VOLUME : return(this.Volume() > obj.Volume() ? 1 : this.Volume() < obj.Volume() ? -1 : 0); case SORT_MODE_POSITION_PRICE_OPEN : return(this.PriceOpen() > obj.PriceOpen() ? 1 : this.PriceOpen() < obj.PriceOpen() ? -1 : 0); case SORT_MODE_POSITION_SL : return(this.SL() > obj.SL() ? 1 : this.SL() < obj.SL() ? -1 : 0); case SORT_MODE_POSITION_TP : return(this.TP() > obj.TP() ? 1 : this.TP() < obj.TP() ? -1 : 0); case SORT_MODE_POSITION_PRICE_CURRENT : return(this.PriceCurrent() > obj.PriceCurrent() ? 1 : this.PriceCurrent() < obj.PriceCurrent() ? -1 : 0); case SORT_MODE_POSITION_SWAP : return(this.Swap() > obj.Swap() ? 1 : this.Swap() < obj.Swap() ? -1 : 0); case SORT_MODE_POSITION_PROFIT : return(this.Profit() > obj.Profit() ? 1 : this.Profit() < obj.Profit() ? -1 : 0); case SORT_MODE_POSITION_SYMBOL : return(this.Symbol() > obj.Symbol() ? 1 : this.Symbol() < obj.Symbol() ? -1 : 0); case SORT_MODE_POSITION_COMMENT : return(this.Comment() > obj.Comment() ? 1 : this.Comment() < obj.Comment() ? -1 : 0); case SORT_MODE_POSITION_EXTERNAL_ID : return(this.ExternalID() > obj.ExternalID() ? 1 : this.ExternalID() < obj.ExternalID() ? -1 : 0); case SORT_MODE_POSITION_TIME_CLOSE : return(this.TimeClose() > obj.TimeClose() ? 1 : this.TimeClose() < obj.TimeClose() ? -1 : 0); case SORT_MODE_POSITION_TIME_CLOSE_MSC : return(this.TimeCloseMsc() > obj.TimeCloseMsc() ? 1 : this.TimeCloseMsc() < obj.TimeCloseMsc() ? -1 : 0); case SORT_MODE_POSITION_PRICE_CLOSE : return(this.PriceClose() > obj.PriceClose() ? 1 : this.PriceClose() < obj.PriceClose() ? -1 : 0); default : return -1; } }
取引オブジェクトクラスを開発する際にも同様のメソッドが検討されました。ここではすべてが同様です。現在のオブジェクトのプロパティ値が比較対象の値より大きい場合は1、小さい場合は-1、プロパティが等しい場合は0が返されます。
以下は、ミリ秒単位で時間を返すメソッドです。
//+------------------------------------------------------------------+ //| Return time with milliseconds | //+------------------------------------------------------------------+ string CPosition::TimeMscToString(const long time_msc, int flags=TIME_DATE|TIME_MINUTES|TIME_SECONDS) const { return(::TimeToString(time_msc/1000, flags) + "." + ::IntegerToString(time_msc %1000, 3, '0')); }
これは、取引クラスで考慮される取引オブジェクトクラスの同名メソッドのコピーです。
以下は、オープン取引へのポインタを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the pointer to the opening deal | //+------------------------------------------------------------------+ CDeal *CPosition::GetDealIn(void) const { int total=this.m_list_deals.Total(); for(int i=0; i<total; i++) { CDeal *deal=this.m_list_deals.At(i); if(deal==NULL) continue; if(deal.Entry()==DEAL_ENTRY_IN) return deal; } return NULL; }
取引オブジェクトのリストで、Entry Inポジション更新メソッドを使用して取引を見つける必要があります。この取引は時間順に並べられたリストの最初にあるはずです。したがって、リストの先頭、つまりインデックス0からサイクルを開始します。
リストからインデックスによって取引を取得し、これが市場参入取引である場合は、リスト内で見つかった取引へのポインタを返します。ループの最後に、取引が見つからないためNULLを返します。
以下は、クローズ取引へのポインタを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the pointer to the close deal | //+------------------------------------------------------------------+ CDeal *CPosition::GetDealOut(void) const { for(int i=this.m_list_deals.Total()-1; i>=0; i--) { CDeal *deal=this.m_list_deals.At(i); if(deal==NULL) continue; if(deal.Entry()==DEAL_ENTRY_OUT || deal.Entry()==DEAL_ENTRY_OUT_BY) return deal; } return NULL; }
ここでは、前のメソッドと比較して、すべてが逆になります。ポジションを終了する取引はリストの一番最後にあるため、最後からループを開始します。
ポジションを終了する取引が見つかったらすぐに、その取引へのポインタを返します。それ以外の場合はNULLを返します。
以下は、履歴ポジションの追加プロパティを取得するメソッドです。
//+------------------------------------------------------------------+ //| Return the open deal ticket | //+------------------------------------------------------------------+ ulong CPosition::DealIn(void) const { CDeal *deal=this.GetDealIn(); return(deal!=NULL ? deal.Ticket() : 0); } //+------------------------------------------------------------------+ //| Return the close deal ticket | //+------------------------------------------------------------------+ ulong CPosition::DealOut(void) const { CDeal *deal=this.GetDealOut(); return(deal!=NULL ? deal.Ticket() : 0); } //+------------------------------------------------------------------+ //| Return the close time | //+------------------------------------------------------------------+ datetime CPosition::TimeClose(void) const { CDeal *deal=this.GetDealOut(); return(deal!=NULL ? deal.Time() : 0); } //+------------------------------------------------------------------+ //| Return the close time in milliseconds | //+------------------------------------------------------------------+ long CPosition::TimeCloseMsc(void) const { CDeal *deal=this.GetDealOut(); return(deal!=NULL ? deal.TimeMsc() : 0); } //+------------------------------------------------------------------+ //| Return the close price | //+------------------------------------------------------------------+ double CPosition::PriceClose(void) const { CDeal *deal=this.GetDealOut(); return(deal!=NULL ? deal.Price() : 0); }
すべてのメソッドでは、まず取引へのポインタを取得し、そこから対応するプロパティを取得する必要があります。ポインタが有効な場合はプロパティ値を返し、そうでない場合は0を返します。
以下は、ポイント単位の利益を返すメソッドです。
//+------------------------------------------------------------------+ //| Return a profit in points | //+------------------------------------------------------------------+ int CPosition::ProfitInPoints(void) const { //--- If symbol Point has not been received previously, inform of that and return 0 if(this.m_point==0) { ::Print("The Point() value could not be retrieved."); return 0; } //--- Get position open and close prices double open =this.PriceOpen(); double close=this.PriceClose(); //--- If failed to get the prices, return 0 if(open==0 || close==0) return 0; //--- Depending on the position type, return the calculated value of the position profit in points return int(this.TypePosition()==POSITION_TYPE_BUY ? (close-open)/this.m_point : (open-close)/this.m_point); }
ここでは、まず対応する取引から始値と終値を取得し、次にポジションの方向に応じて利益計算の結果をポイントで返します。
以下は、取引リストに取引を追加するメソッドです。
//+------------------------------------------------------------------+ //| Add a deal to the list of deals | //+------------------------------------------------------------------+ CDeal *CPosition::DealAdd(const long ticket) { //--- A temporary object gets a ticket of the desired deal and the flag of sorting the list of deals by ticket this.m_temp_deal.SetTicket(ticket); this.m_list_deals.Sort(SORT_MODE_DEAL_TICKET); //--- Set the result of checking if a deal with such a ticket is present in the list bool added=(this.m_list_deals.Search(&this.m_temp_deal)!=WRONG_VALUE); //--- Set the flag of sorting by time in milliseconds for the list this.m_list_deals.Sort(SORT_MODE_DEAL_TIME_MSC); //--- If a deal with such a ticket is already in the list, return NULL if(added) return NULL; //--- Create a new deal object CDeal *deal=new CDeal(ticket); if(deal==NULL) return NULL; //--- Add the created object to the list in sorting order by time in milliseconds //--- If failed to add the deal to the list, remove the the deal object and return NULL if(!this.m_list_deals.InsertSort(deal)) { delete deal; return NULL; } //--- If this is a position closing deal, set the profit from the deal properties to the position profit value and //--- create a connecting line between the position open-close deal labels if(deal.Entry()==DEAL_ENTRY_OUT || deal.Entry()==DEAL_ENTRY_OUT_BY) { this.SetProfit(deal.Profit()); this.CreateLine(); } //--- Return the pointer to the created deal object return deal; }
このメソッドは、選択された取引のチケットを受け取ります。まず、リスト内にそのようなチケットの取引がないことを確認します。これをおこなうには、メソッドに渡されたチケットを一時的な取引オブジェクトに割り当てます。取引リストは取引チケットの値によって並び替えられれ、そのようなチケットを含むリスト内の取引の検索が実行されます。取引を検索した直後に、ミリ秒単位の時間で並び替えるフラグがリストに返されます。そのような取引がすでにリストにある場合は、それを追加する必要はありません。メソッドはNULLを返します。同時に、取引リストの並べ替えはデフォルトでミリ秒単位の時間で設定されています。そのような取引がリストにない場合は、取引オブジェクトが新規作成され、ミリ秒単位の時間で並び替えられた順序でリストに追加されます。これがポジションクローズ取引である場合、その利益はポジションのプロパティに設定され、ポジションのオープン取引とクローズ取引の間に接続線が作成されます。その結果、新しく作成された取引オブジェクトへのポインタが返されます。
以下は、ポジションタイプの説明を返すメソッドです。
//+------------------------------------------------------------------+ //| Return a position type description | //+------------------------------------------------------------------+ string CPosition::TypeDescription(void) const { return(this.m_type==POSITION_TYPE_BUY ? "Buy" : this.m_type==POSITION_TYPE_SELL ? "Sell" : "Unknown::"+(string)this.m_type); }
m_type変数に格納されているポジションのタイプに応じて説明を返します。変数値がポジションタイプの列挙定数と一致しない場合は、「Unknown::」+variable_valueを返します。
以下は、ポジションの終了時間と価格の説明を返すメソッドです。
//+------------------------------------------------------------------+ //| Return position close time and price description | //+------------------------------------------------------------------+ string CPosition::TimePriceCloseDescription(void) { if(this.TimeCloseMsc()==0) return "Not closed yet"; return(::StringFormat("Closed %s [%.*f]", this.TimeMscToString(this.TimeCloseMsc()),this.m_digits, this.PriceClose())); }
ポジションオブジェクトにまだ決済取引がない場合、メソッドはポジションがまだ決済されていないというメッセージを返します。それ以外の場合は、次のタイプの文字列が作成され、返されます。
Closed 2023.06.12 17:04:20.362 [1.07590]
以下は、ポジション開始時間と価格の説明を返すメソッドです。
//+------------------------------------------------------------------+ //| Return position open time and price description | //+------------------------------------------------------------------+ string CPosition::TimePriceOpenDescription(void) { return(::StringFormat("Opened %s [%.*f]", this.TimeMscToString(this.TimeMsc()),this.m_digits, this.PriceOpen())); }
ここでは、次のタイプの文字列が作成され、返されます。
Opened 2023.06.12 16:51:36.838 [1.07440]
以下は、ポジションの簡単な説明を返すメソッドです。
//+------------------------------------------------------------------+ //| Return a brief position description | //+------------------------------------------------------------------+ string CPosition::Description(void) { return(::StringFormat("Position %s %.2f %s #%I64d, Magic %I64d", this.Symbol(), this.Volume(), this.TypeDescription(), this.ID(), this.Magic())); }
次のタイプの文字列が作成され、返されます。
Position EURUSD 0.10 Buy #1752955040, Magic 123
以下は、ポジションのポップアップメッセージのテキストを返す仮想メソッドです。
//+------------------------------------------------------------------+ //| Return a text of a position pop-up message | //+------------------------------------------------------------------+ string CPosition::Tooltip(void) { //--- Get the pointers to the open and close deals CDeal *deal_in =this.GetDealIn(); CDeal *deal_out=this.GetDealOut(); //--- If no deals are received, return an empty string if(deal_in==NULL || deal_out==NULL) return NULL; //--- Get commission, swap and deal fee that are common for two deals double commission=deal_in.Commission()+deal_out.Commission(); double swap=deal_in.Swap()+deal_out.Swap(); double fee=deal_in.Fee()+deal_out.Fee(); //--- Get the final profit of the position and the spread values when opening and closing double profit=deal_out.Profit(); int spread_in=deal_in.Spread(); int spread_out=deal_out.Spread(); //--- If the reason for closing the position is StopLoss, TakeProfit or StopOut, set the reason description in the variable string reason=(deal_out.Reason()==DEAL_REASON_SL || deal_out.Reason()==DEAL_REASON_TP || deal_out.Reason()==DEAL_REASON_SO ? deal_out.ReasonDescription() : ""); //--- Create and return the tooltip string return(::StringFormat("%s\nCommission %.2f, Swap %.2f, Fee %.2f\nSpread In/Out %d/%d, Profit %+.2f %s (%d points)\nResult: %s %+.2f %s", this.Description(), commission, swap, fee, spread_in, spread_out, profit,this.m_currency_profit, this.ProfitInPoints(), reason, profit+commission+fee+swap, this.m_currency_profit)); }
次のタイプの文字列が作成され、返されます。
Position EURUSD 0.10 Buy #1752955040, Magic 0 Commission 0.00, Swap 0.00, Fee 0.00 Spread In/Out 0/0, Profit +15.00 USD (150 points) Result: TP +15.00 USD
テキストは、ポジションのオープン取引とクローズ取引を結ぶ文字列のツールチップテキストとして設定されます。このメソッドは仮想であり、わずかに異なるデータを出力する必要がある場合は、継承されたクラスでオーバーライドできます。メソッドによって返される文字列をツールチップテキストとして使用する場合、文字列の長さを考慮する必要があります。ツールチップは、制御コードを含めて約160文字に制限されています。残念ながら、経験的に発見された正確な値を指定することはできません。
以下は、チャート上に取引ラベルを表示するメソッドです。
//+------------------------------------------------------------------+ //| Display deal labels on the chart | //+------------------------------------------------------------------+ void CPosition::ShowDeals(const bool chart_redraw=false) { for(int i=this.m_list_deals.Total()-1; i>=0; i--) { CDeal *deal=this.m_list_deals.At(i); if(deal==NULL) continue; deal.ShowArrow(); } if(chart_redraw) ::ChartRedraw(this.m_chart_id); }
取引リストをループして、各取引を取得し、受信したオブジェクトのShowArrow()メソッドを呼び出して、チャートに取引ラベルを表示します。ループの終了時に、適切なフラグが設定されている場合はチャートを再描画します。
以下は、チャート上の取引ラベルを非表示にするメソッドです。
//+------------------------------------------------------------------+ //| Hide deal labels on the chart | //+------------------------------------------------------------------+ void CPosition::HideDeals(const bool chart_redraw=false) { for(int i=this.m_list_deals.Total()-1; i>=0; i--) { CDeal *deal=this.m_list_deals.At(i); if(deal==NULL) continue; deal.HideArrow(); } if(chart_redraw) ::ChartRedraw(this.m_chart_id); }
取引リストをループして、各取引を取得し、受信したオブジェクトのHideArrow()メソッドを呼び出して、チャートから取引ラベルを非表示にします。ループの終了時に、適切なフラグが設定されている場合はチャートを再描画します。
以下は、オープン取引とクローズ取引を結ぶ線を作成するメソッドです。
//+------------------------------------------------------------------+ //| Create a line connecting open-close deals | //+------------------------------------------------------------------+ bool CPosition::CreateLine(void) { //--- If the graphical line object could not be created, report this in the journal and return 'false' ::ResetLastError(); if(!::ObjectCreate(this.m_chart_id, this.m_line_name, OBJ_TREND, 0, 0, 0, 0, 0)) { ::Print("ObjectCreate() failed. Error ", ::GetLastError()); return false; } //--- Hide the line this.HideLine(); //--- Set the line to be drawn with dots, define the color and return 'true' ::ObjectSetInteger(this.m_chart_id, this.m_line_name, OBJPROP_STYLE, STYLE_DOT); ::ObjectSetInteger(this.m_chart_id, this.m_line_name, OBJPROP_COLOR, this.m_line_color); return true; }
0位置(価格は0、時間は01.01.1970 00:00:00)に価格と時間の座標を持つ線が作成されます。グラフから線を非表示にし、描画スタイルをドットとデフォルトの色に設定します。
最初は、各オブジェクトの線は非表示で作成されます。表示が必要な時点で、必要な座標と表示が次のように設定されます。
以下は、取引ラベル間の接続線を表示するメソッドです。
//+------------------------------------------------------------------+ //| Display the connecting line between deal labels | //+------------------------------------------------------------------+ void CPosition::ShowLine(const bool chart_redraw=false) { //--- Get the pointers to the open and close deals CDeal *deal_in= this.GetDealIn(); CDeal *deal_out=this.GetDealOut(); //--- If no deals are received, leave if(deal_in==NULL || deal_out==NULL) return; //--- Set a start and end time, a price from the deal properties and a tooltip text for the line ::ObjectSetInteger(this.m_chart_id, this.m_line_name, OBJPROP_TIME, 0, deal_in.Time()); ::ObjectSetInteger(this.m_chart_id, this.m_line_name, OBJPROP_TIME, 1, deal_out.Time()); ::ObjectSetDouble(this.m_chart_id, this.m_line_name, OBJPROP_PRICE, 0, deal_in.Price()); ::ObjectSetDouble(this.m_chart_id, this.m_line_name, OBJPROP_PRICE, 1, deal_out.Price()); ::ObjectSetString(this.m_chart_id, this.m_line_name, OBJPROP_TOOLTIP, this.Tooltip()); //--- Show the line on the chart and update it if the flag is set ::ObjectSetInteger(this.m_chart_id, this.m_line_name, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS); if(chart_redraw) ::ChartRedraw(this.m_chart_id); }
以下は、取引ラベル間の接続線を非表示にするメソッドです。
//+------------------------------------------------------------------+ //| Hide the connecting line between the deal labels | //+------------------------------------------------------------------+ void CPosition::HideLine(const bool chart_redraw=false) { ::ObjectSetInteger(this.m_chart_id, this.m_line_name, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS); if(chart_redraw) ::ChartRedraw(this.m_chart_id); }
オブジェクトの可視性フラグはすべてのチャート期間でリセットされ、フラグが設定されている場合はチャートが更新されます。
以下は、接続線の色を設定するメソッドです。
//+------------------------------------------------------------------+ //| Set the color of the connecting line | //+------------------------------------------------------------------+ void CPosition::SetLineColor(const color clr=C'225,68,29') { if(::ObjectSetInteger(this.m_chart_id, this.m_line_name, OBJPROP_COLOR, clr)) this.m_line_color=clr; }
メソッドに渡された値は、オブジェクトの色プロパティとして設定されます。すべてが成功した場合、色はm_line_color変数に設定されます。
以下は、売買取引の色を設定するメソッドです。
//+------------------------------------------------------------------+ //| Set Buy and Sell deal color | //+------------------------------------------------------------------+ void CPosition::SetDealsColor(const color clr_deal_buy=C'3,95,172', const color clr_deal_sell=C'225,68,29') { //--- In the loop by the list of deals int total=this.m_list_deals.Total(); for(int i=0; i<total; i++) { //--- get the next deal object CDeal *deal=this.m_list_deals.At(i); if(deal==NULL) continue; //--- In case of Buy deal type, set a color for a Buy deal for the object if(deal.TypeDeal()==DEAL_TYPE_BUY) deal.SetColorArrow(clr_deal_buy); //--- In case of Sell deal type, set a color for a Sell deal for the object if(deal.TypeDeal()==DEAL_TYPE_SELL) deal.SetColorArrow(clr_deal_sell); } }
メソッドは、買い取引と売り取引の色を受け取ります。ポジション取引のリストをループして、各連続取引へのポインタを取得し、取引の種類に応じて取引ラベルの色を設定します。
以下は、チャート上のポジションのグラフィック表現を表示するメソッドです。
//+------------------------------------------------------------------+ //| Display a graphical representation of a position on a chart | //+------------------------------------------------------------------+ void CPosition::Show(const bool chart_redraw=false) { this.ShowDeals(false); this.ShowLine(chart_redraw); }
このメソッドは、チャート上でエントリ取引とエグジット取引、およびそれらを結ぶ線を表示するprotectedメソッドを順番に呼び出します。
以下は、チャート上のポジションのグラフィック表現を非表示にするメソッドです。
//+------------------------------------------------------------------+ //| Hide a graphical representation of a position on a chart | //+------------------------------------------------------------------+ void CPosition::Hide(const bool chart_redraw=false) { this.HideLine(false); this.HideDeals(chart_redraw); }
このメソッドは、エントリ取引とエグジット取引、およびチャート上でそれらを結ぶ線を非表示にするprotectedメソッドを順番に呼び出します。
上記で検討した2つのメソッドはpublicで、制御プログラムからチャート上のポジションの表示を制御するために使用されます。
以下は、ポジションのプロパティと取引を操作ログに出力するメソッドです。
//+------------------------------------------------------------------+ //| Print the position properties and deals in the journal | //+------------------------------------------------------------------+ void CPosition::Print(void) { ::PrintFormat("%s\n-%s\n-%s", this.Description(), this.TimePriceOpenDescription(), this.TimePriceCloseDescription()); 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(); } }
まず、ポジションの簡単な説明と、ポジションの開始と終了の時間と価格を示す2行を含むヘッダーが表示されます。次に、リストのすべてのポジション取引の説明がループで印刷されます。
その結果、操作ログには次のデータが表示されます。
Position EURUSD 0.10 Sell #2523224572, Magic 0 -Opened 2024.05.31 17:06:15.134 [1.08734] -Closed 2024.05.31 17:33:17.772 [1.08639] Deal: Entry In 0.10 Sell #2497852906 at 2024.05.31 17:06:15.134 Deal: Entry Out 0.10 Buy #2497993663 at 2024.05.31 17:33:17.772
あまり参考になりませんが、これらのクラスのPrint()メソッドはデバッグ専用に作成されました。
現在、取引クラスとポジションクラスの2つのクラスがすでに存在しています。ポジションクラスには、ポジションの存続期間中に参加した取引のリストが含まれています。クラスはシンプルで、取引に関する基本情報と、価格、ポジションの開始/終了時間、ポイントでの利益に関する追加データが含まれています。残りのメソッドは、この情報を取得してグラフまたはテキストラインに表示するために使用されます。
ここで、すべてのポジションを取引から収集し、履歴ポジションのリストに配置する一般的なクラスを作成しましょう。このクラスは、ポジションとその取引のプロパティへのアクセスを提供し、履歴ポジションのリストを更新および補足し、指定されたポジションをチャートに表示します。
履歴ポジション管理クラス
前の2つのクラスを作成した同じフォルダに、CPositionsControlクラスのファイルPositionsControl.mqhを新規作成します。
クラスはCObject標準ライブラリの基底オブジェクトから継承する必要があり、ポジションクラスファイルはCPositionsControlの新しいクラスファイルに含まれます。
//+------------------------------------------------------------------+ //| PositionsControl.mqh | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include "Position.mqh" //+------------------------------------------------------------------+ //| Class of historical positions | //+------------------------------------------------------------------+ class CPositionsControl : public CObject { }
クラスを処理するための変数とメソッドをprivate、protected、publicセクションで宣言します。
//+------------------------------------------------------------------+ //| Class of historical positions | //+------------------------------------------------------------------+ class CPositionsControl : public CObject { private: string m_symbol; // The symbol the position is open for long m_current_id; // ID of the current position displayed on the chart bool m_key_ctrl; // Flag for allowing to control the chart using the keyboard //--- Return the position type by deal type ENUM_POSITION_TYPE PositionTypeByDeal(const CDeal *deal); protected: CPosition m_temp_pos; // Temporary position object for searching CArrayObj m_list_pos; // List of positions long m_chart_id; // Chart ID //--- Return the position object from the list by ID CPosition *GetPositionObjByID(const long id); //--- Return the flag of the market position bool IsMarketPosition(const long id); //--- Return the pointer to the (1) first and the (2) last open position in the list CPosition *GetFirstClosedPosition(void); CPosition *GetLastClosedPosition(void); //--- Return the pointer to the (1) previous and (2) next closed position in the list CPosition *GetPrevClosedPosition(CPosition *current); CPosition *GetNextClosedPosition(CPosition *current); //--- Displays a graphical representation of the specified position on a chart void Show(CPosition *pos, const bool chart_redraw=false); //--- Center the chart on the currently selected position void CentersChartByCurrentSelected(void); //--- Return the ID of the current selected position long CurrentSelectedID(void) const { return this.m_current_id; } //--- Return the selected position (1) open and (2) close time datetime TimeOpenCurrentSelected(void); datetime TimeCloseCurrentSelected(void); //--- Hide the graphical representation of all positions on the chart except the specified one void HideAllExceptOne(const long pos_id, const bool chart_redraw=false); public: //--- Return the chart (1) symbol and (2) ID string Symbol(void) const { return this.m_symbol; } long ChartID(void) const { return this.m_chart_id; } //--- Create and update the list of positions. It can be redefined in the inherited classes virtual bool Refresh(void); //--- Return the number of positions in the list int Total(void) const { return this.m_list_pos.Total(); } //--- (1) Hide and (2) display the graphical representation of the first position on the chart void HideFirst(const bool chart_redraw=false); void ShowFirst(const bool chart_redraw=false); //--- (1) Hide and (2) display the graphical representation of the last position on the chart void HideLast(const bool chart_redraw=false); void ShowLast(const bool chart_redraw=false); //--- Display a graphical representation of the (1) current, (2) previous and (3) next position void ShowCurrent(const bool chart_redraw=false); void ShowPrev(const bool chart_redraw=false); void ShowNext(const bool chart_redraw=false); //--- Return the description of the currently selected position string CurrentSelectedDescription(void); //--- Print the properties of all positions and their deals in the journal void Print(void); //--- Constructor/destructor CPositionsControl(const string symbol=NULL); ~CPositionsControl(); };
宣言されたメソッドについて詳しく考えてみましょう。
クラスコンストラクタ
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CPositionsControl::CPositionsControl(const string symbol=NULL) { this.m_list_pos.Sort(SORT_MODE_POSITION_TIME_CLOSE_MSC); this.m_symbol = (symbol==NULL ? ::Symbol() : symbol); this.m_chart_id = ::ChartID(); this.m_current_id = 0; this.m_key_ctrl = ::ChartGetInteger(this.m_chart_id, CHART_KEYBOARD_CONTROL); }
履歴ポジションのリストの終了時間(ミリ秒単位)で並び替えるフラグを設定します。コンストラクタに渡された銘柄を変数に設定します。これが空の文字列の場合、プログラムが実行されているチャートの現在の銘柄になります。チャートIDは現在のチャートIDとして設定されます。継承したクラスに異なるIDを設定することが可能になります。m_key_ctrl変数に、キーを使用してチャートを制御できるようにするフラグを設定します。プログラムの実行後、この値はチャートのプロパティに設定され直され、プログラムが起動される前の状態に戻ります。
クラスのデストラクタでは、ポジションのリストが破棄され、CHART_KEYBOARD_CONTROLチャートプロパティはプログラムの起動前に存在していた値を取得します。
//+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CPositionsControl::~CPositionsControl() { this.m_list_pos.Shutdown(); ::ChartSetInteger(this.m_chart_id, CHART_KEYBOARD_CONTROL, this.m_key_ctrl); }
以下は、IDによってリストからポジションオブジェクトを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the position object from the list by ID | //+------------------------------------------------------------------+ CPosition *CPositionsControl::GetPositionObjByID(const long id) { //--- Set the position ID for the temporary object and set the flag of sorting by position ID for the list this.m_temp_pos.SetID(id); this.m_list_pos.Sort(SORT_MODE_POSITION_IDENTIFIER); //--- Get the index of the position object with such an ID (or -1 if it is absent) from the list //--- Use the obtained index to get the pointer to the positino object from the list (or NULL if the index value is -1) int index=this.m_list_pos.Search(&this.m_temp_pos); CPosition *pos=this.m_list_pos.At(index); //--- Set the flag of sorting by position close time in milliseconds for the list and //--- return the pointer to the position object (or NULL if it is absent) this.m_list_pos.Sort(SORT_MODE_POSITION_TIME_CLOSE_MSC); return pos; }
メソッドのロジックはコードのコメントで完全に説明されています。
以下は、市場ポジションフラグを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the market position flag | //+------------------------------------------------------------------+ bool CPositionsControl::IsMarketPosition(const long id) { //--- In a loop by the list of current positions in the terminal for(int i=::PositionsTotal()-1; i>=0; i--) { //--- get the position ticket by the loop index ulong ticket=::PositionGetTicket(i); //--- If the ticket is received, the position can be selected and its ID is equal to the one passed to the method, //--- this is the desired market position, return 'true' if(ticket!=0 && ::PositionSelectByTicket(ticket) && ::PositionGetInteger(POSITION_IDENTIFIER)==id) return true; } //--- No such market position, return 'false' return false; }
取引リストからポジションのリストを作成するときは、現在の市場ポジションを考慮せず、それらを履歴ポジションのリストに追加しないようにすることが重要です。ポジションがまだクローズされていないことを確認するには、アクティブポジションのリストでそのポジションをIDを探す必要があります。そのようなポジションが存在する場合、リストに追加する必要はありません。このメソッドは、チケットによって市場ポジションのリストからポジションを検索し、ポジションIDがメソッドに渡された値と一致するかどうかを確認し、そのようなポジションが存在する場合(選択可能)、trueを返します。それ以外の場合はfalseを返します。
以下は、リスト内の最初のクローズポジションへのポインタを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the pointer to the first closed position in the list | //+------------------------------------------------------------------+ CPosition *CPositionsControl::GetFirstClosedPosition(void) { this.m_list_pos.Sort(SORT_MODE_POSITION_TIME_CLOSE_MSC); return this.m_list_pos.At(0); }
ポジションのリストは、ポジションの終了時間(ミリ秒単位)で並び替えられ、リストの最初のポジション(最も古いポジション)へのポインタが返されます。
以下は、リスト内の最後のクローズポジションへのポインタを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the pointer to the last closed position in the list | //+------------------------------------------------------------------+ CPosition *CPositionsControl::GetLastClosedPosition(void) { this.m_list_pos.Sort(SORT_MODE_POSITION_TIME_CLOSE_MSC); return this.m_list_pos.At(this.m_list_pos.Total()-1); }
ポジションのリストは、ポジションの終了時間(ミリ秒単位)で並び替えられ、リスト内の最後のポジションへのポインタが返されます。
以下は、リスト内の前のクローズポジションへのポインタを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the pointer to the previous closed position in the list | //+------------------------------------------------------------------+ CPosition *CPositionsControl::GetPrevClosedPosition(CPosition *current) { this.m_list_pos.Sort(SORT_MODE_POSITION_TIME_CLOSE_MSC); int prev=this.m_list_pos.SearchLess(current); return this.m_list_pos.At(prev); }
メソッドに渡されるポインタの現在選択されているポジションに基づいて、リスト内で前のポジションが検索され、終了時間(ミリ秒単位)で並び替えられます。CArrayObjクラスのSearchLess()メソッドは、リストの並べ替えの基準となる、リスト内で最初に検出された小さい値を持つオブジェクトへのポインタを返します。この場合、リストは終了時間(ミリ秒単位)で並び替えられます。したがって、メソッドに渡された終了時間よりも短い終了時間で見つかった最初のオブジェクトが前のポジションになります。ポジションオブジェクトが見つからない場合、またはリストの最初の要素がメソッドに渡された場合(それより前の要素がない場合)、メソッドはNULLを返します。
以下は、リスト内の次のクローズポジションへのポインタを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the pointer to the next closed position in the list | //+------------------------------------------------------------------+ CPosition *CPositionsControl::GetNextClosedPosition(CPosition *current) { this.m_list_pos.Sort(SORT_MODE_POSITION_TIME_CLOSE_MSC); int next=this.m_list_pos.SearchGreat(current); return this.m_list_pos.At(next); }
メソッドに渡されるポインタの現在選択されているポジションに基づいて、リスト内で次のポジションが検索され、終了時間(ミリ秒単位)で並び替えられます。CArrayObjクラスのSearchGreat()メソッドは、リストの並べ替えの基準となる、リスト内で最初に検出された、より高い値を持つオブジェクトへのポインタを返します。この場合、リストは終了時間(ミリ秒単位)で並び替えられます。したがって、メソッドに渡された終了時間よりも大きい終了時間を持つ最初のオブジェクトが次のポジションになります。ポジションオブジェクトが見つからない場合、またはリストの最後の要素がメソッドに渡された場合(次の要素がない)、メソッドはNULLを返します。
以下は、チャート上の特定のポジションのグラフィック表現を表示するメソッドです。
//+----------------------------------------------------------------------------+ //| Display a graphical representation of the specified position on the chart | //+----------------------------------------------------------------------------+ void CPositionsControl::Show(CPosition *pos,const bool chart_redraw=false) { if(pos!=NULL) { pos.Show(chart_redraw); this.m_current_id=pos.ID(); } }
このメソッドは、チャート上にグラフィック表現を表示するポジションへのポインタを受け取ります。有効なオブジェクトを受信した場合は、そのShow()メソッドを呼び出して、ポジションIDをm_current_id変数に追加します。m_current_id変数内のポジションIDによって現在選択されているポジションを定義できます。ポジションのグラフィック表現がチャート上に表示されている場合、そのポジションは選択されたとみなされます。
以下は、チャートから最初のポジションのグラフィック表現を非表示にするメソッドです。
//+------------------------------------------------------------------------+ //| Hide the graphical representation of the first position from the chart | //+------------------------------------------------------------------------+ void CPositionsControl::HideFirst(const bool chart_redraw=false) { CPosition *pos=this.GetFirstClosedPosition(); if(pos!=NULL) pos.Hide(chart_redraw); }
リストの最初のポジションへのポインタを取得し、そのHide()メソッドを呼び出します。
以下は、チャート上の最初のポジションのグラフィック表現を表示するメソッドです。
//+---------------------------------------------------------------------------+ //| Display the graphical representation of the first position on the chart | //+---------------------------------------------------------------------------+ void CPositionsControl::ShowFirst(const bool chart_redraw=false) { //--- Get the pointer to the first closed position CPosition *pos=this.GetFirstClosedPosition(); if(pos==NULL) return; //--- Hide labels of all positions except the first one by its ID this.HideAllExceptOne(pos.ID()); //--- Display the labels of the first position on the chart and //--- center the chart by the labels of the currently selected position this.Show(pos,chart_redraw); this.CentersChartByCurrentSelected(); }
上記のGetFirstClosedPosition()メソッドを使用して、ミリ秒単位の時間で並び替えられたリストの最初のポジションへのポインタを取得します。最初のポジションを除くすべてのポジションのラベルを非表示にし、最初のポジションを表示して、チャート上のその取引のラベルを中央に配置します。
以下は、チャート上の最後のポジションのグラフィック表現を非表示にするメソッドです。
//+------------------------------------------------------------------+ //| Hide graphical representation of the last position from the chart| //+------------------------------------------------------------------+ void CPositionsControl::HideLast(const bool chart_redraw=false) { CPosition *pos=this.GetLastClosedPosition(); if(pos!=NULL) pos.Hide(chart_redraw); }
リストの最後のポジションへのポインタを取得し、そのHide()メソッドを呼び出します。
以下は、チャート上の最後のポジションのグラフィック表現を表示するメソッドです。
//+-----------------------------------------------------------------------+ //| Display the graphical representation of the last position on the chart| //+-----------------------------------------------------------------------+ void CPositionsControl::ShowLast(const bool chart_redraw=false) { //--- Get the pointer to the last closed position CPosition *pos=this.GetLastClosedPosition(); if(pos==NULL) return; //--- Hide labels of all positions except the last one by its ID this.HideAllExceptOne(pos.ID(), false); //--- Display the labels of the last position on the chart and //--- center the chart by the labels of the currently selected position this.Show(pos,chart_redraw); this.CentersChartByCurrentSelected(); }
上記のGetLastClosedPosition()メソッドを使用して、リスト内の最後のポジションへのポインタをミリ秒単位の時間で並び替えて取得します。最後のポジションを除くすべてのポジションのラベルを非表示にし、最後のポジションを表示して、チャート上の取引のラベルを中央に配置します。
以下は、チャート上の現在のポジションのグラフィック表現を表示するメソッドです。
//+--------------------------------------------------------------------------+ //| Display a graphical representation of the current position on the chart | //+--------------------------------------------------------------------------+ void CPositionsControl::ShowCurrent(const bool chart_redraw=false) { //--- Get a pointer to the currently selected closed position CPosition *curr=this.GetPositionObjByID(this.CurrentSelectedID()); if(curr==NULL) return; //--- Display the labels of the current position on the chart and //--- center the chart by the labels of the currently selected position this.Show(curr,chart_redraw); this.CentersChartByCurrentSelected(); }
m_current_id変数にIDが設定されているポジションへのポインタを取得し、そのグラフィック表現をチャート上に表示し、チャート上の取引ラベルでチャートを中央に配置します。
以下は、チャート上の前のポジションのグラフィック表現を表示するメソッドです。
//+------------------------------------------------------------------------+ //|Display a graphical representation of the previous position on the chart| //+------------------------------------------------------------------------+ void CPositionsControl::ShowPrev(const bool chart_redraw=false) { //--- Get the pointer to the current and previous positions CPosition *curr=this.GetPositionObjByID(this.CurrentSelectedID()); CPosition *prev=this.GetPrevClosedPosition(curr); if(curr==NULL || prev==NULL) return; //--- Hide the current position, display the previous one and //--- center the chart by the labels of the currently selected position curr.Hide(); this.Show(prev,chart_redraw); this.CentersChartByCurrentSelected(); }
ここで、リスト内の現在選択されているポジションと前のポジションへのポインタを取得します。現在のポジションのラベルを非表示にし、前のポジションのラベルを表示します。これらが表示されると、ポジションIDがm_current_id変数に設定され、このポジションが現在のポジションであることを示します。ラベルを使用してグラフを中央に配置します。
以下は、チャート上の次のポジションのグラフィック表現を表示するメソッドです。
//+---------------------------------------------------------------------+ //| Display a graphical representation of the next position on the chart| //+---------------------------------------------------------------------+ void CPositionsControl::ShowNext(const bool chart_redraw=false) { //--- Get the pointer to the current and next positions CPosition *curr=this.GetPositionObjByID(this.CurrentSelectedID()); CPosition *next=this.GetNextClosedPosition(curr); if(curr==NULL || next==NULL) return; //--- Hide the current position, display the next one and //--- center the chart by the labels of the currently selected position curr.Hide(); this.Show(next,chart_redraw); this.CentersChartByCurrentSelected(); }
このメソッドは前のメソッドと同じですが、ここではリスト内の現在選択されているポジションと次のポジションへのポインタを取得する点が異なります。現在のポジションのラベルを非表示にして、次のポジションのアイコンを表示します。これらが表示されると、ポジションIDがm_current_id変数に設定され、このポジションが現在のポジションであることを示します。ラベルを使用してグラフを中央に配置します。
以下は、指定されたポジションを除くチャート上のすべてのポジションののグラフィック表現を非表示にするメソッドです。
//+------------------------------------------------------------------+ //| Hide the graphical representation | //| of all positions except the specified one | //+------------------------------------------------------------------+ void CPositionsControl::HideAllExceptOne(const long pos_id,const bool chart_redraw=false) { //--- In a loop by the list of positions int total=this.m_list_pos.Total(); for(int i=0; i<total; i++) { //--- get the pointer to the next position and CPosition *pos=this.m_list_pos.At(i); if(pos==NULL || pos.ID()==pos_id) continue; //--- hide the graphical representation of the position pos.Hide(); } //--- After the loop, update the chart if the flag is set if(chart_redraw) ::ChartRedraw(this.m_chart_id); }
このメソッドは、チャート上にラベルを残す必要があるポジションのIDを受け取ります。他のすべてのポジションのラベルは非表示になります。
以下は、現在選択されているポジションにチャートを中央揃えするメソッドです。
//+------------------------------------------------------------------+ //| Center the chart at the currently selected position | //+------------------------------------------------------------------+ void CPositionsControl::CentersChartByCurrentSelected(void) { //--- Get the index of the first visible bar on the chart and the number of visible bars int bar_open=0, bar_close=0; int first_visible=(int)::ChartGetInteger(this.m_chart_id, CHART_FIRST_VISIBLE_BAR); int visible_bars =(int)::ChartGetInteger(this.m_chart_id, CHART_VISIBLE_BARS); //--- Get the position opening time and use it to get the opening bar datetime time_open=this.TimeOpenCurrentSelected(); if(time_open!=0) bar_open=::iBarShift(this.m_symbol, PERIOD_CURRENT, time_open); //--- Get the position opening time and use it to get the closing bar datetime time_close=this.TimeCloseCurrentSelected(); if(time_close!=0) bar_close=::iBarShift(this.m_symbol, PERIOD_CURRENT, time_close); //--- Calculate the width of the window the deal labels are located in int width=bar_open-bar_close; //--- Calculate the chart offset so that the window with deals is in the center of the chart int shift=(bar_open + visible_bars/2 - width/2); //--- If the window width is greater than the chart width, the opening deal is located on the second visible bar if(shift-bar_open<0) shift=bar_open+1; //--- If the deal opening bar is to the left of the first visible bar of the chart //--- or the deal opening bar is to the right of the chart last visible bar, //--- scroll the chart by the calculated offset if(bar_open>first_visible || bar_open<first_visible+visible_bars) ::ChartNavigate(this.m_chart_id, CHART_CURRENT_POS, first_visible-shift); }
メソッドのロジック全体は、コードのコメントに完全に記述されています。この方法では、銘柄チャートをシフトして、視覚的に接続線のすべてのポジション取引がチャートの中央に配置されるようにしています。すべての取引のラベルがチャートの幅内に収まらない場合は、ポジションを開く取引がチャートの左から2番目の表示バーに表示されるようにチャートがシフトされます。
以下は、現在選択されているポジションの開始時刻を返すメソッドです。
//+------------------------------------------------------------------+ //| Return the opening time of the currently selected position | //+------------------------------------------------------------------+ datetime CPositionsControl::TimeOpenCurrentSelected(void) { CPosition *pos=this.GetPositionObjByID(this.CurrentSelectedID()); return(pos!=NULL ? pos.Time() : 0); }
m_current_id変数に設定されたIDによって、現在選択されているポジションへのポインタを取得します。ポインタが正常に受信された場合は、ポジションオープン時間を返します。それ以外の場合は0を返します。
以下は、現在選択されているポジションの終了時刻を返すメソッドです。
//+------------------------------------------------------------------+ //| Return the current selected position closing time | //+------------------------------------------------------------------+ datetime CPositionsControl::TimeCloseCurrentSelected(void) { CPosition *pos=this.GetPositionObjByID(this.CurrentSelectedID()); return(pos!=NULL ? pos.TimeClose() : 0); }
m_current_id変数に設定されたIDによって、現在選択されているポジションへのポインタを取得します。ポインタが正常に受信された場合は、ポジションの終了時間を返します。それ以外の場合は0を返します。
以下は、取引の種類によってポジションタイプを返すメソッドです。
//+------------------------------------------------------------------+ //| Return position type by deal type | //+------------------------------------------------------------------+ ENUM_POSITION_TYPE CPositionsControl::PositionTypeByDeal(const CDeal *deal) { if(deal==NULL) return WRONG_VALUE; switch(deal.TypeDeal()) { case DEAL_TYPE_BUY : return POSITION_TYPE_BUY; case DEAL_TYPE_SELL : return POSITION_TYPE_SELL; default : return WRONG_VALUE; } }
メソッドは取引へのポインタを受け取ります。取引の種類に応じて、適切なポジションタイプを返します。
以下は、履歴ポジションのリストを作成するメソッドです。
//+------------------------------------------------------------------+ //| Create historical position list | //+------------------------------------------------------------------+ bool CPositionsControl::Refresh(void) { //--- If failed to request the history of deals and orders, return 'false' if(!::HistorySelect(0,::TimeCurrent())) return false; //--- Set the flag of sorting by time in milliseconds for the position list this.m_list_pos.Sort(SORT_MODE_POSITION_TIME_MSC); //--- 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=total-1; i>=0; i--) { //--- get the ticket of the next deal in the list ulong ticket=::HistoryDealGetTicket(i); //--- If the deal ticket is not received, or it is not a buy/sell deal, or if the deal is not for the symbol set for the class, move on ENUM_DEAL_TYPE deal_type=(ENUM_DEAL_TYPE)::HistoryDealGetInteger(ticket, DEAL_TYPE); if(ticket==0 || (deal_type!=DEAL_TYPE_BUY && deal_type!=DEAL_TYPE_SELL) || ::HistoryDealGetString(ticket, DEAL_SYMBOL)!=this.m_symbol) continue; //--- Get the value of the position ID from the deal long pos_id=::HistoryDealGetInteger(ticket, DEAL_POSITION_ID); //--- If this is a market position, move on if(this.IsMarketPosition(pos_id)) continue; //--- Get the pointer to a position object from the list pos=this.GetPositionObjByID(pos_id); //--- If there is no position with this ID in the list yet if(pos==NULL) { //--- Create a new position object and, if the object could not be created, add 'false' to the 'res' variable and move on pos=new CPosition(pos_id, this.m_symbol); if(pos==NULL) { res &=false; continue; } //--- If failed to add the position object to the list, add 'false' to the 'res' variable, remove the position object and move on if(!this.m_list_pos.InsertSort(pos)) { res &=false; delete pos; continue; } } //--- If the deal object could not be added to the list of deals of the position object, add 'false' to the 'res' variable and move on CDeal *deal=pos.DealAdd(ticket); if(deal==NULL) { res &=false; continue; } //--- All is successful. //--- Set position properties depending on the deal type if(deal.Entry()==DEAL_ENTRY_IN) { pos.SetTime(deal.Time()); pos.SetTimeMsc(deal.TimeMsc()); ENUM_POSITION_TYPE type=this.PositionTypeByDeal(deal); pos.SetTypePosition(type); pos.SetPriceOpen(deal.Price()); pos.SetVolume(deal.Volume()); } if(deal.Entry()==DEAL_ENTRY_OUT || deal.Entry()==DEAL_ENTRY_OUT_BY) { pos.SetPriceCurrent(deal.Price()); } if(deal.Entry()==DEAL_ENTRY_INOUT) { ENUM_POSITION_TYPE type=this.PositionTypeByDeal(deal); pos.SetTypePosition(type); pos.SetVolume(deal.Volume()-pos.Volume()); } } //--- Set the flag of sorting by close time in milliseconds for the position list this.m_list_pos.Sort(SORT_MODE_POSITION_TIME_CLOSE_MSC); //--- Return the result of creating and adding a position to the list return res; }
メソッドのロジックは、コードコメントで詳しく説明されています。
過去のポジションを検索するロジックを簡単に見てみましょう。クライアント端末には現在のポジションのリストのみが含まれています。各ポジションには、履歴取引リストに独自の取引があります。各取引には、参加したポジションのIDが含まれます。このIDは、取引が属するポジションを決定するために使用できます。したがって、履歴ポジションのリストを作成するには、履歴取引のリストをループし、IDを使用して取引が属するポジションを特定する必要があります。このようなIDを持つポジションがアクティブなポジションのリストに含まれていないことを確認してください。もし含まれていれば、まだポジションがクローズされていないため、取引はスキップする必要があります。そのようなポジションが市場に存在しない場合は、そのようなIDを持つ履歴ポジションオブジェクトを新規作成し、作成されたポジションの取引リストにこの取引を追加する必要があります。ポジションオブジェクトを作成する前に、そのようなポジションが以前に作成されているかどうかを確認する必要があります。作成されている場合は、ポジションオブジェクトを作成する必要はありません。代わりに、この取引をすでに作成されたポジションのリストに追加するだけです。もちろん、そのような取引がすでにポジションリストにある場合は、追加する必要はありません。過去の取引サイクルが完了すると、結果としてすでに決済されたポジションのリストが作成され、このリスト内の各ポジションにはその取引のリストが含まれます。
以下は、ポジションとその取引のプロパティを操作ログに出力するメソッドです。
//+------------------------------------------------------------------+ //| Print the properties of positions and their deals in the journal | //+------------------------------------------------------------------+ void CPositionsControl::Print(void) { int total=this.m_list_pos.Total(); for(int i=0; i<total; i++) { CPosition *pos=this.m_list_pos.At(i); if(pos==NULL) continue; pos.Print(); } }
最も早いポジション(リストの最初から)から始めて、各連続ポジションを取得し、その説明をループで操作ログに出力します。
以下は、選択された各ポジションの説明を返すメソッドです。
//+------------------------------------------------------------------+ //| Return the description of the currently selected position | //+------------------------------------------------------------------+ string CPositionsControl::CurrentSelectedDescription(void) { CPosition *pos=this.GetPositionObjByID(this.CurrentSelectedID()); return(pos!=NULL ? pos.Tooltip() : NULL); }
m_current_id変数にIDが設定されている現在選択されているポジションへのポインタを取得し、ツールチップを表示するために作成された文字列を返します。この文字列はチャートのコメントに表示するために使用できます。チャート上にラベルが表示されているポジションのいくつかのプロパティを視覚的に表示するために、テストEAで表示してみましょう。テストEAには次の機能が含まれます。
- 起動すると、過去のポジションのリストの形式で取引履歴が作成されます。各ポジションにはその取引のリストが含まれます。
- 履歴ポジションのリストが完了すると、最後にクローズされたポジションが、線でつながれたオープンラベルとクローズラベルとしてチャートに表示されます。
- Ctrlキーを押しながらカーソルキーを使用して、履歴ポジションのリスト内を移動できます。
- 左矢印キーを押すと、チャート上の前のポジションを表示が表示される
- 右矢印キーを押すと、チャート上の次のポジションを表示が表示される
- 上矢印キーを押すと、チャート上の最初のポジションが表示される
- 下矢印キーを押すと、チャート上のさいゴのポジションが表示される
- Shiftキーを押したままにすると、リスト内で現在選択されているポジションを説明するコメントがチャート上に表示されます。
検証
\MQL5\Experts\PositionsViewer\で、EAファイルPositionViewer.mq5を新規作成します。
履歴ポジション管理クラスのファイルをインクルードし、キーコードとEAグローバル変数のマクロ置換を宣言します。
//+------------------------------------------------------------------+ //| PositionViewer.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include <PositionsViewer\PositionsControl.mqh> #define KEY_LEFT 37 #define KEY_RIGHT 39 #define KEY_UP 38 #define KEY_DOWN 40 //--- global variables CPositionsControl ExtPositions; // Historical position class instance bool ExtChartScroll; // Chart scrolling flag bool ExtChartHistory; // Trading history display flag //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+
EAのOnInit()ハンドラで、次のコードを設定します。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Save the chart auto scroll flag and disable auto scroll ExtChartScroll=ChartGetInteger(ChartID(), CHART_AUTOSCROLL); ChartSetInteger(ChartID(), CHART_AUTOSCROLL, false); //--- Save the trading history display flag and disable history display ExtChartHistory=ChartGetInteger(ChartID(), CHART_SHOW_TRADE_HISTORY); ChartSetInteger(ChartID(), CHART_SHOW_TRADE_HISTORY, false); //--- Create a list of closed positions and display the list creation time in the journal ulong start=GetTickCount64(); Print("Reading trade history and creating a list of historical positions"); ExtPositions.Refresh(); ulong msec=GetTickCount64()-start; PrintFormat("List of historical positions created in %I64u msec", msec); //ExtPositions.Print(); //--- If this is a launch after changing the chart period, display the currently selected position if(UninitializeReason()==REASON_CHARTCHANGE) ExtPositions.ShowCurrent(true); //--- otherwise, display the last closed position else ExtPositions.ShowLast(true); //--- Successful return(INIT_SUCCEEDED); }
チャートの自動スクロールや取引履歴の表示のフラグは、プログラム終了時に復元するためにここに保存されます。チャートの自動スクロールと履歴表示は無効になり、現在の銘柄に存在したすべての履歴ポジションのリストが作成され、リストの作成時刻が操作ログに表示されます。「//ExtPositions.Print();」のコメントを解除すると、クローズされたポジションのリストを作成した後、作成されたリストのすべての履歴ポジションが操作ログに表示されます。チャートの時間枠の変更でない場合は、最後にクローズしたポジションのラベルがチャートに描画され、ラベルが中央になるようにチャートがスクロールされます。チャート期間の変更の場合は、リスト内で現在選択されているポジションが表示されます。
OnDeinit()ハンドラで、チャートの自動スクロールと取引履歴表示の保存された値を復元し、チャートからコメントを削除します。
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Restore the auto scroll and trading history property initial value and remove chart comments ChartSetInteger(ChartID(), CHART_AUTOSCROLL, ExtChartScroll); ChartSetInteger(ChartID(), CHART_SHOW_TRADE_HISTORY, ExtChartHistory); Comment(""); }
OnTradeTransaction()ハンドラでは、新しい取引が発生するたびに、履歴ポジション制御クラスのリストを更新するメソッドを呼び出します。ポジションがクローズされている場合は、チャートにそのラベルを表示します。
//+------------------------------------------------------------------+ //| TradeTransaction function | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) { //--- In case of a transaction type, add a new deal if(trans.type==TRADE_TRANSACTION_DEAL_ADD) { //--- update the list of positions and their deals ExtPositions.Refresh(); //--- Get the new deal ticket ulong deal_ticket=trans.deal; //--- If the ticket is not received or failed to get the method for updating the position from the deal properties, leave long entry; if(deal_ticket==0 || !HistoryDealGetInteger(deal_ticket, DEAL_ENTRY, entry)) return; //--- If this is an exit deal, display the last closed position on the chart if(entry==DEAL_ENTRY_OUT || entry==DEAL_ENTRY_OUT_BY) ExtPositions.ShowLast(true); } }
イベントハンドラでは、キーの押下イベントを追跡し、Ctrlキーを押しながらカーソルキーを押下してクローズポジションのリスト内を移動することに応答します。また、Shiftキーを押すと、チャートのコメントにポジションの説明が表示され、チャートのスケールを変更して現在のポジションラベルでチャートを中央に配置することもできます。
//+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- If the event ID is pressing a key if(id==CHARTEVENT_KEYDOWN) { //--- If the Ctrl key is held down if(TerminalInfoInteger(TERMINAL_KEYSTATE_CONTROL)<0) { //--- If the chart scrolling with keys is active, disable it if((bool)ChartGetInteger(0, CHART_KEYBOARD_CONTROL)) ChartSetInteger(0, CHART_KEYBOARD_CONTROL, false); //--- If the left key is pressed, display the previous closed position if(lparam==KEY_LEFT) ExtPositions.ShowPrev(true); //--- If the right key is pressed, display the next closed position if(lparam==KEY_RIGHT) ExtPositions.ShowNext(true); //--- If the up key is pressed, display the first closed position if(lparam==KEY_UP) ExtPositions.ShowFirst(true); //--- If the down key is pressed, display the last closed position if(lparam==KEY_DOWN) ExtPositions.ShowLast(true); } //--- If Ctrl is not pressed, else { //--- If the chart scrolling with keys is inactive, enable it if(!(bool)ChartGetInteger(0, CHART_KEYBOARD_CONTROL)) ChartSetInteger(0, CHART_KEYBOARD_CONTROL, true); } } //--- If the Shift key is held down, display a description of the current position in the chart comment if(TerminalInfoInteger(TERMINAL_KEYSTATE_SHIFT)<0) Comment(ExtPositions.CurrentSelectedDescription()); //--- If the Shift key is not pressed, check the comment on the chart and delete it if it is not empty else { if(ChartGetString(ChartID(),CHART_COMMENT)!=NULL) Comment(""); } //--- If the horizontal scale of the chart has changed, display the currently selected position static int scale=-1; if(id==CHARTEVENT_CHART_CHANGE) { int scale_curr=(int)ChartGetInteger(ChartID(), CHART_SCALE); if(scale!=scale_curr) { ExtPositions.ShowCurrent(true); scale=scale_curr; } } }
これは、取引履歴の標準表示によるクローズされたポジションの配置です。
これは、記事の冒頭に書かれていたことをよく表しています。
それでは、EAをコンパイルしてチャート上で起動してみましょう。
起動時には、標準的な方法で表示される履歴は非表示になっており、最後にクローズされたポジションのみが表示されていました。Ctrlキーを押しながらカーソルキーを押すと、ポジションの履歴を移動できます。クローズされたポジションのリストを移動しながら、ここでクローズされたポジションのすべての取引のラベルがチャートに表示された場所で、取引履歴のグラフィック表現を簡単に見ることができます。
Shiftキーも押すと、現在選択されているクローズポジションの説明がチャートのコメントに表示されます。
ここで、取引が互いに十分な距離を置いて配置されているポジションを見つけ、水平スケールを拡大したときにチャートウィンドウの1つの表示領域に収まらない場合に、チャートの中央に配置される方法を確認してみましょう。
両方の取引がチャートウィンドウに収まらない場合は、左から2番目のバーのポジションに入る取引が見えるように中央に配置されることがわかります。
Shiftキーを押すと、チャートのコメントにこのクローズポジションの説明が表示されます。
これは、取引を結ぶ線の上にマウスカーソルを合わせなくても、ポジションの説明を確認できるため便利です。Ctrl + Shiftキーを押しながらカーソルキーを使用してクローズポジションのリスト内を移動すると、チャート上に現在表示されているクローズポジションの説明を明確に確認できます。
EAを起動すると、履歴ポジションのリストの作成が開始されます。私の場合、リスト作成時の2023年からの短い履歴は次のとおりです。
PositionViewer (EURUSD,M15) Reading trade history and creating a list of historical positions
PositionViewer (EURUSD,M15) List of historical positions created in 6422 msec
以降のチャート期間の切り替えでは、リストを再作成する時間は不要になります。
PositionViewer (EURUSD,M1) Reading trade history and creating a list of historical positions PositionViewer (EURUSD,M1) List of historical positions created in 31 msec PositionViewer (EURUSD,M5) Reading trade history and creating a list of historical positions PositionViewer (EURUSD,M5) List of historical positions created in 47 msec PositionViewer (EURUSD,M1) Reading trade history and creating a list of historical positions PositionViewer (EURUSD,M1) List of historical positions created in 31 msec
結論
作成されたプログラムによって提供される取引履歴は、アクティブで頻繁な取引の場合に便利です。チャートがクローズされたポジションのラベルでオーバーロードされることはありません。各ポジションは独立して表示され、現在のポジションと次のポジションまたは前のポジションの表示の切り替えはカーソルキーでおこなわれます。各取引ラベルのツールチップには、標準の取引履歴よりも多くの情報が含まれています。取引を結ぶ線の上にマウスを置くと、クローズされたポジションに関する情報を含むツールチップが表示されます。
この記事で作成されたクラスは、独自の開発で使用して機能を拡張し、取引履歴やクローズされたポジションの履歴に関する情報を含む、より複雑で機能的なプログラムを作成できます。
すべてのクラスファイルとテストEAは記事に添付されており、独立した学習に利用できます。MQL5.zipアーカイブには、MQL5端末ディレクトリにすぐに解凍できるような形式でファイルが含まれており、ExpertsフォルダにPositionsViewer\フォルダが新規作成され、すべてのプロジェクトファイル(PositionViewer.mq5 EAファイルと3つのクラスファイル)が含められます。EAファイルをコンパイルして使用することもできます。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/15026





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索