MetaTrader 5 の注文、ポジション、取引

23 12月 2015, 11:11
MetaQuotes Software Corp.
0
1 492

取引条件

トレーダーの最終的なゴールは、金融マーケットにおいてトレーディングを行い利益を得ることにあります。この記事は、トレーディング用プラットフォームである、メタトレーダー5 における手順と使用規定について述べられており、 MQL5言語の取引関数の仕組みを適切に理解する上で、必要な知識を得ることができます。<

  • 注文 — メタトレーダー5の規定に応じて作動するトレーディングサーバーから 受け取ったトレード処理リクエストです。もしそのリクエストが間違っている場合、注文としてトレーディングプラットフォーム上に表示されません。成行注文は、特定の金融商品をその時点の値段で一定量購入・販売を行うため、注文は即座に実行されます。その他、指値注文というものがあり、ある特定の条件化にて取引処理 を行うために保持されている注文のことを指します。指値注文には、注文有効期限期日と呼ばれる注文に対する時間制約が存在します。

    メタトレーダー5における注文とポジション

    特定の条件での実行、もしくはキャンセル処理を待つ指値注文は、ターミナル上のTradeタブに表示されます。これらの注文は、修正、キャンセルが可能です。注文の実行、キャンセル、修正は、OrderSend()関数を利用し行います。注文が有効期限切れを迎えたり、またはキャンセルされていた場合、もしくはすでに取引が行われていた場合、その注文は注文履歴に移動されます。実行済み、キャンセル済み注文は、ターミナル上の "History " タブにて表示されます。History上の注文の修正は不可能です。

  • 取引 - 注文実行(取引実行の命令)結果を指します。各取引は、1件の特定注文に対応しますが、1件注文が複数の取引を生成することもあります。例えば、10ロットの購入は、部分的に処理される複数の連続した取引によって実行される場合があります。取引は、常にトレード履歴に蓄積され、修正することはできません。ターミナル上にて、取引は、"Hisotry"タブにて表示されます。

    メタトレーダー5における売買取引

  • ポジションとは、売買された金融商品の契約群を指します。ロングポジション(Long) とは、額の上昇を予想し購入した状態を示し、ショートポジション(Short)とは、資産価格の低下を予期し売却することを示します。各口座において、全ての金融商品はただ一つのポジション状態を保持します。常に、シンボルに対してはロングもしくはショートいずれか一つのポジションになります。

    メタトレーダー5における注文履歴

    同方向での新規のトレーディング処理の結果、ポジションの量が増えることがあります。具体的には、新規購入(買い取引)後、ロングポジションは増加し、販売取引(売り取引)後減少します。トレーディング処理後、購買契約数が0になった場合、ポジションは閉じられます。そのような処理を、クロージング・ポジションと呼びます。

注: 取引中の注文やポジションは常に、"Trade"タブにて表示されます。また、過去の取引や注文はHisotryタブ上にて表示されます。Tradeタブ上の実行中の注文とHistoryタブ上の過去の注文を混同しないようにしなければなりません。


サーバーからのトレード情報受け取り、保持方法

ターミナルはトレーディング履歴を特定の場所に保存し、トレーディングサーバー接続毎に、トレーディング口座上の完了後の注文や取引の中でターミナル上に保持されていないもののみを受信します。トラフィックの節約を目的とし、上記のような仕組みになっています。メタトレーダー5のクライアントターミナルの終了時、また、使用中の口座を変更する際には、全ての履歴がハードディスク上に記録され、次回ターミナル起動時に読み込まれます。

全てのデータベースは、暗号化されて記録され、暗号キーは、端末のインストールされた先のコンピューターに依存します。コピーを目論んだ、データへの権限のないアクセスからターミナルのユーザーを保護することができます。

口座への接続中、ターミナルは口座の履歴を持つアカウントデータベースをダウンロードし、トレーディングサーバー上の口座履歴とローカル上の履歴を同期するリクエストをサーバーに送信します。さらに、口座への接続成功後、トレーディングサーバーにより、口座上の実行中のトレーディングイベントについてのレポートがクライアント側に送信されます。

トレーディングイベントは、口座の以下の変更点を指します。

  • 引き落とし・口座残高表示処理
  • 手数料・スワッピング・税の請求
  • 発注・注文消去・注文修正
  • 注文に基づく取引の実行
  • ポジションのオープン、クローズ
  • ポジションの量と方向の変化

トレーディングサーバーとの接続が中止された場合、ターミナルは周期的に再接続を試みます。サーバーとの再接続後、ターミナルは、履歴データベース上のデータ整合性を維持するため、最近の変更情報全てをリクエストします。

端末のHistoryタブにて表示されるトレード履歴は、ターミナル履歴上のデータベースから取得されます。データベース上に保存されている履歴の範囲は、履歴ターミナルに表示される期間の変更により拡大されます。表示される履歴の時間範囲を減少させても、ターミナルの履歴の物理的消去にはつながりません。

表示するトレーディング履歴のインターバルの導入

より短い間隔での表示履歴の導入が、保存されるトレーディング履歴の量を減らすことにはなりません。しかし、Historyタブの表示に関して、より長い間隔を設定すると、もしターミナルのデータベースがその長期間のデータを保持していなくても、より長い履歴をサーバーにリクエストします。

一般的なターミナルとメタトレーダー5サーバーの連携の仕組みは以下に示されています。

ターミナル起動時、接続失敗後の再接続中、別口座への接続切り替え中、保存されていないトレード履歴依頼中などに、クライアント側は同期リクエストを自身のトレーディング履歴用のデータベースに投げます。

その後、トレーディングサーバーはターミナルからのリクエストなしに、クライアント側に、注文やポジション状態の変化、注文に基づく取引処理、手数料の請求や残高、引き落としなど、口座上のトレーディングイベントに関してのメッセージを送信します。


MQL5プログラムからのトレーディング履歴へのアクセス

ターミナルは、たくさんの指示や、スクリプト、EAなどにそって稼働します。これらプログラムは、注文や取引、ポジションなどトレーディングに関する情報を呼び出すことができます。ターミナルのデータベースとのMQL5プログラムによる直接の連携は、安定性、安全性やパフォーマンスを考慮し、禁止されています

MQL5プログラムは、すべてその動作に対してリクエストベースで キャッシュにトレーディング環境のモデルを受信します。キャッシュは、データへの高速アクセスための特別な記憶領域です。例えば、注文の処理を始める前に、注文はMQL5のキャッシュ上に保存されます。注文を呼び出す際の全ての処理が、キャッシュにコピーされたデータに対して行われます。

履歴上のポジション、取引、注文に関する処理は上記のような方法で実行されます。MQL5プログラムから取得されるトレーディング情報の一般的な仕組みは図に示されています。

トレーディング履歴のデータがMQL5プログラムの処理にかけられる際は、ターミナル上のデータベースからリクエストされなければなりません。リクエスト後、取得されたデータはMQL5のキャッシュに格納されます。

注: キャッシュ内のデータは、ターミナル上のデータベースと自動的に同期されません。それゆえ、キャッシュ内のデータの状態を適切なものにしておくために頻繁に更新される必要があります。

キャッシュが不適切に使用される可能性もあります。

  • リクエストされたデータが取得されなかった場合、キャッシュは空になり、必要なデータを保持しない状態になります。
  • キャッシュ内のデータの更新が必要だが、更新がリクエストされなかった場合、そのようなデータによる処理は不本意な結果を生み出す可能性があります。例えば、現在のポジションに関するデータが更新されず、指定されたシンボルについてプログラムがオープンポジションであることや、拡大する損失について知らないという事態が発生する可能性があります。


キャッシュとの連携関数

トレーディング履歴が、MQL5プログラムの稼働に必要のない何千もの実行済み注文や取引を保持している場合があります。キャッシュとの連携は、ターミナルとの直近の接続時に更新された情報をキャッシュが常に持つというリクエストの原則のもとに成り立ちます。注文や取引の全履歴が必要な場合、必要なインターバルを明確に設定し、リクエストをする必要があります。

それぞれの情報の種類に応じて、独立したキャッシュが生成されます。注文に関するデータは、注文用のキャッシュに、ポジションに関する情報はポジション用のキャッシュに、取引や注文に関するデータは、それぞれのキャッシュの履歴に保存されています。

キャッシュに情報をリクエストする前に、キャッシュ内にデータが保持されている必要があります。

注: キャッシュに情報を格納するためのリクエストは、実行結果に関係なく前もってキャッシュを消去します。

トレーディング関数は、キャッシュに情報を格納する関数と、キャッシュ内の情報を読み込む関数の二つのカテゴリに分類されます。


キャッシュ内への格納関数

トレーディング履歴の処理のため、まず履歴情報は取得され、適切なキャッシュ内に格納される必要があります。キャッシュを生成する関数は、二つの下位グループに分類されます。

トレーディングキャッシュへの格納用関数(取引中の注文とポジション)

  • OderSelect(ticket)関数は、引数のチケットに応じて注文をコピーし、OrderGetDouble()、OrderGetInteger()やOrderGetString()関数を使用した注文の情報へのリクエストのために、キャッシュ内に保存します。
  • OrderGetTicket(index)関数は、ターミナル上のデータベースから注文リスト内の注文のインデックスごとに OrderGetDouble()、OrderGetINteger、OrgerGetString()関数による注文情報の取得のため、現注文の今後のリクエストのためにキャッシュ内に保存します。ターミナル内の注文の合計数はOrrdersTotal() 関数により求めることができます。
  • PositionSelect(symbol)関数は、(ターミナル内のデータベースから取得した)名前をもとにオープンポジションをシンボル名ごとに コピーし、PositionGetDouble()や、PositionGetInteger()、PositionGetString()関数によるさらなるプロパティのリクエストのためキャッシュ内にコピーします。
  • PositionGetSymbol(index)は、ポジションリスト内のポジションを ポジションリストのインデックスごと にコピーし、PositionGetDOuble()、PositionGetInteger()、PositionGetString()関数による情報の取得リクエストのためにキャッシュ内にコピーします。ターミナル内の合計ポジション数は、PositionsTotal()関数により取得することができます。

履歴キャッシュへの格納関数

  • OderSelect(ticket)- 履歴オーダーのキャッシュにチケット ごとに履歴注文をコピーし(ターミナルベースから)、関数 HistoryOrderGetInteger() および HistoryOrderGetString() による後のそのプロパティ呼び出しに備えます。
  • HistoryDealSelect(ticket)- ディールのキャッシュにチケット ごとにディールをコピーし(ターミナルベースから)、関数 HistoryDealGetDouble()、HistoryDealGetInteger()、 HistoryDealGetString() による後のそのプロパティ呼び出しに備えます。

キャッシュ内、 一般的トレーディング履歴のデータに影響を与える二つの関数を分けて考える必要があります。

  • HistorySelect(start, end) -サーバー時間の指定間隔でディールとオーダーでヒストリーキャッシュに書き込みをします。この関数の実行結果は HistoryDealsTotal() および HistoryOrdersTotal()から返される値に依存します。
  • HistorySelectByPosition (position_ID) -指定の識別子ポジションを持ってディールとオーダーでヒストリーキャッシュに書き込みをします。この関数の実行結果も HistoryDealsTotal() および HistoryOrdersTotal() に影響します。

OrderSelect と OrderGetTicket

OrderSelect(ticket) 関数やOrderGetTicket()関数は、 同様な処理を行います。-アクティブなオーダーのキャッシュ内に一つの注文情報を格納します。 OrderSelect(ticket)関数は、 チケット注文が前もって知られているような場合に用いられます。OrdersTotal() と連携する OrderGetTicket()は、注文情報のデータベース内の全ての注文の検査を行います。

これらの関数を呼び出し、オーダーが適切に選択された場合、注文情報に関するキャッシュは注文一つのみ格納された状態になります。さもなければ、アクティブな-オーダーのキャッシュは空になります。OrdersTotal() 関数の実行結果は変化せず、キャッシュに情報のあるなしに関係なく、常にターミナルデータベース内の注文数を返します。

PositionSelect と PositionGetSymbol

注文関連の関数同様に、これら二つの関数はポジションに作動し、一つのポジションをポジション用のキャッシュ内に格納します。 PositionGetSymbol(index) 関数は、ポジション用データベースのリストにおけるインデックスを変数として要求し、PositionSelect(symbol) 関数は、オープン状態のポジションをシンボル名単位に格納します。シンボル名は、PositionGetSymbol(index)関数によって取得されます。

これらの関数実行後、ポジション用のキャッシュは、関数の実行が正常に行われた場合、ポジションデータ一つのみを格納した状態になります。さもなければ、ポジションのキャッシュは空になります。 PositionTotal()関数の実行結果は、キャッシュにデータが格納されているかに影響されず、常に全シンボルについてターミナルデータベース内のオープンポジション数を返します。

HistoryOrderSelect

HistoryOrderSelect(ticket) ターミナルベース内から引数に応じてキャッシュ内に取引情報を取得します。この関数は前もって引数であるチケットを取得している場合に使用されます。

実行が正常に行われた場合、キャッシュには一つの取引情報が保持され、 HisotryDealsTotal()関数は、1を返します。さもなければ、取引用キャッシュは空になり、HisotryDealsTotal()関数はゼロを返します。

HistoryDealSelect

The HistoryDealSelect(ticket) は、ターミナル内からティケットに応じて取引情報を取得します。この関数は前もって引数であるチケットを取得している場合に使用されます。

実行に成功した場合、キャッシュには一つの注文情報を保持し、HistoryDealsTotal() 関数は1を返します。もなければ、過去の注文用キャッシュは、空になり、 HistoryDealsTotal() 関数は0を返します。


キャッシュから情報を取得する関数

ポジションや取引、注文の属性に関する情報をリクエストする前に mql5-programプログラムの対応するキャッシュを更新する必要があります。リクエストされた情報はすでにキャッシュ内で更新されている可能性があり、すでにキャッシュ内に格納されているコピーは古い情報であるからです。

  • 注文 注文情報を取得するためには、まず OrderGetTicket() か OrderSelect の二つの関数によりアクティブ注文データがキャッシュ内にコピーされている必要があります。プロパティ値はキャッシュ内に格納されている注文データに関して、対応する関数が呼ばれる際に取得されます。
    1. OrderGetDouble(type_property)
    2. OrderGetInteger(type_property)
    3. OrderGetString(type_property)

これらの関数はキャッシュ内の全てのデータを取得します。従って、正確な注文データの取得を保証するためには、キャッシュにデータを格納する関数を呼ぶことが推奨されています。

  • ポジション

    ポジションについての情報を取得するためには、PositionGetSymbolやPositionSelect の二つの関数のうち一つを呼び、事前に選択し、キャッシュ内にコピーされる必要があります。プロパティ値はキャッシュ内に格納されている注文データに関して、対応する関数が呼ばれる際に取得されます。

    1. PositionGetDouble(type_property)
    2. PositionGetInteger(type_property)
    3. PositionGetString(type_property)

これらの関数はキャッシュ内の全てのデータを取得します。従って、正確な注文データの取得を保証するためには、キャッシュにデータを格納する関数を呼ぶことが推奨されています。

  • 過去の注文

    履歴内の注文情報を取得するためには、HistorySelect(start,end)、HisotrySelectByPosition()、HistoryOrderSelect(ticket) の関数のうち一つを呼び出し、注文履歴用のキャッシュを生成する必要があります。正常に実装することができた場合、HistoryOrdersTotal() 関数によって返された注文数がキャッシュ内に格納されます。以下の適切な関数によって、チケットエレメントごとにこれらの注文データへのアクセスが実行されます。

    1. HistoryOrderGetDouble(ticket_order, type_property)
    2. HistoryOrderGetInteger(ticket_order, type_property)
    3. HistoryOrderGetString(ticket_order, type_property)

過去の注文のチケットは、HistoryOrderGetTicket(index) 関数に過去の注文用キャッシュ内のインデックスを用い、見つけることができます。正確な注文データの取得を保証するためには、キャッシュにデータを格納する関数を呼ぶことが推奨されています。

  • ディール

    履歴内の特定のディール情報を取得するためには、HistorySelect(start,end)、HisotrySelectByPosition()、HistoryOrderSelect(ticket) の3つの関数のうち一つを呼び出し、ディールのキャッシュを生成する必要があります。正常に実装することができた場合、HistoryDealsTotal() 関数によって返されたディールがキャッシュ内に格納されます。以下の適切な関数によって、チケットを基ににこれらのディールへのアクセスが実行されます。

    1. HistoryDealGetDouble(ticket_deals, type_property)
    2. HistoryDealGetInteger(ticket_deals, type_property)
    3. HistoryDealGetString(ticket_deals, type_property)

ディールのチケットは、HistoryDealGetTicket(index) 関数にディールキャッシュ内のインデックスを用い、見つけることができます。ディールに関する正確なデータの取得を保証するためには、キャッシュにデータを格納する関数を呼ぶことが推奨されています。

キャッシュ履歴からチケットを取得する関数

HistoryOrderGetTicket (index) は、履歴オーダーの キャッシュから(ターミナルデータベースからではありません!)インデックスごとに履歴オーダーのチケットを返します。取得されたチケットは、成功時キャッシュ内を消去 し、一つの注文データを再格納するHistoryOrderSelec<w4>t(ticket)関数で使用されます。 HistoryOrdersTotal() 関数から返される値はキャッシュ内の注文数に依存することを思い出してください。

HistoryDealGetTicket(index) はディールの キャッシュからインデックスごとにディールのチケットを返します。ディールのチケットは、成功時1つのディールについてのみキャッシュ内を消去 し、一つの注文データを再格納するHistoryDealSelect(ticket)関数で使用されます。HistoryDealsTotal() 関数から返される値はキャッシュ内のディール数に依存します。

注:HistoryOrderGetTicket (index) および HistoryDealGetTicket (index) 関数を呼び出す前に、履歴用キャッシュに十分な 量の過去の注文、ディールを格納する必要があります。そのために、HistorySelect (start, end)、HistorySelectByPosition (position_ID)、HistoryOrderSelect (ticket)、HistoryDealSelect (ticket) の関数の内一つを呼び出してください。


アクティブオーダーによる情報取得

現在アクティブな注文をチェックすることは標準的な手順です。あつ特定のオーダーに関する情報を取得するためには そのチケットを知るることが必要です。これは関数 OrderSelect(ticket) によって行います。

bool selected=OrderSelect(ticket);
if(selected)
  {
   double price_open=OrderGetDouble(ORDER_PRICE_OPEN);
   datetime time_setup=OrderGetInteger(ORDER_TIME_SETUP);
   string symbol=OrderGetString(ORDER_SYMBOL);
   PrintFormat("Ордер #%d for %s was set at %s",ticket,symbol,TimeToString(time_setup));
  }
else
  {
   PrintFormat("Error selecting order with ticket %d. Error %d",ticket, GetLastError());
  }

上記の例では、注文のチケットデータは前もって取得されており、たとえば、グローバル変数から取得されています。しかしながら、一般的にはチケットデータは取得されず、一つ注文を選択し、キャッシュ内に格納するOrderGetTicket(index) 関数の助けを借りる必要があります。ただ、注文リスト内の注文番号のみパラメータとして指定される必要があります。

注文(取引やポジションに関しても同様)データにおける処理に関する全体的なアルゴリズムは以下になります。

  1. OrdersTotal() 関数を使用し、<w0>注文の合計数を取得します。
  2. リスト内のインデックスを用いて全ての注文を検索するループを作成します。
  3. OrderGetTicket() 関数を使用し、一つ一つの注文を順番にキャッシュ内に保存します。
  4. 関数 OrderGetDouble()、OrderGetInteger()、OrderGetString() 関を使用し、キャッシュ内から正確な注文データを取得します。必要であれば、取得されたデータを分析し、適切な処理を行います。

以下がアルゴリズムの簡単な例です。

input long my_magic=555;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- obtain the total number of orders
   int orders=OrdersTotal();
//--- scan the list of orders
   for(int i=0;i<orders;i++)
     {
      ResetLastError();
      //--- copy into the cache, the order by its number in the list
      ulong ticket=OrderGetTicket(i);
      if(ticket!=0)// if the order was successfully copied into the cache, work with it
        {
         double price_open  =OrderGetDouble(ORDER_PRICE_OPEN);
         datetime time_setup=OrderGetInteger(ORDER_TIME_SETUP);
         string symbol      =OrderGetString(ORDER_SYMBOL);
         long magic_number  =OrderGetInteger(ORDER_MAGIC);
         if(magic_number==my_magic)
           {
            //  process the order with the specified ORDER_MAGIC
           }
         PrintFormat("Order #%d for %s was set out %s, ORDER_MAGIC=%d",ticket,symbol,TimeToString(time_setup),magic_number);
        }
      else         // call OrderGetTicket() was completed unsuccessfully
        {
         PrintFormat("Error when obtaining an order from the list to the cache. Error code: %d",GetLastError());
        }
     }
  }


オープンポジション情報の取得

常にオープンポジションを監視することは標準的ではありませんが、大抵の場合行われる手順です。特定ポジション情報を取得するためには、そのポジションを開くインストルメント名が分かれば十分です。それにはPositionSelect(symbol)関数を用います。EAが一つのシンボル(帰属するチャートのシンボル)だけに対応して動作する場合、シンボル名はSymbol() 関数、または定義済み変数である_Symbolから取得されます。

//--- we will look for the position by the symbol of the chart, on which the EA is working
   string symbol=Symbol();
//--- attempt to get the position
   bool selected=PositionSelect(symbol);
   if(selected) // if the position is selected
     {
      long pos_id            =PositionGetInteger(POSITION_IDENTIFIER);
      double price           =PositionGetDouble(POSITION_PRICE_OPEN);
      ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
      long pos_magic         =PositionGetInteger(POSITION_MAGIC);
      string comment         =PositionGetString(POSITION_COMMENT);
      PrintFormat("Position #%d by %s: POSITION_MAGIC=%d, price=%G, type=%s, commentary=%s",
                 pos_id, symbol, pos_magic, price,EnumToString(type), comment);
     }

   else        // if selecting the position was unsuccessful
     {
      PrintFormat("Unsuccessful selection of the position by the symbol %s. Error",symbol,GetLastError());
     }
  }

通常の場合、シンボルに関する情報は PositionGetSymbol (index) 関数を用いて取得可能です。これはポジションを一つ選択し、キャッシュに配置する関数です。パラメータとして、オープンポジションリスト上の位置を示すインデックスを特定する必要があります。ループ内の全ポジション検索を行うにはこれが最良の方法です。

ポジションと連携する全般的アルゴリズム

  1. PositionsTotal() 関数を使ってポジションの合計数を取得します。
  2. リスト上のインデックスごとに全ポジション検索を行うことでループを生成します。
  3. PositionGetSymbol() 関数を使ってポジションを一つずつキャッシュにコピーします
  4. PositionGetDouble()、PositionGetInteger()、PositionGetString() 関数を使い、必要なポジションデータをキャッシュから取得します。 必要であれば、取得されたデータを分析し、適切な処理を行います。

アルゴリズムの例:

#property script_show_inputs

input long my_magic=555;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- obtain the total number of positions
   int positions=PositionsTotal();
//--- scan the list of orders
   for(int i=0;i<positions;i++)
     {
      ResetLastError();
      //--- copy into the cache, the position by its number in the list
      string symbol=PositionGetSymbol(i); //  obtain the name of the symbol by which the position was opened
      if(symbol!="") // the position was copied into the cache, work with it
        {
         long pos_id            =PositionGetInteger(POSITION_IDENTIFIER);
         double price           =PositionGetDouble(POSITION_PRICE_OPEN);
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
         long pos_magic         =PositionGetInteger(POSITION_MAGIC);
         string comment         =PositionGetString(POSITION_COMMENT);
         if(pos_magic==my_magic)
           {
           //  process the position with a specified POSITION_MAGIC
           }
         PrintFormat("Position #%d by %s: POSITION_MAGIC=%d, price=%G, type=%s, commentary=%s",
                     pos_id,symbol,pos_magic,price,EnumToString(type),comment);
        }
      else           // call to PositionGetSymbol() was unsuccessful
        {
         PrintFormat("Error when receiving into the cache the position with index %d."+
                     " Error code: %d", i, GetLastError());
        }
     }
  }


履歴キャッシュと連携する際のルール

プログラマーによって書かれる履歴キャッシュと連携するためのコードはたいてい、履歴が5~10件の取引と注文を持っているだけならうまくいく、というものです。よくある誤ったアプローチの例をあげます。トレーディング履歴全体をキャッシュにロード、ループで処理し、注文と取引全件から検索する、というものです。

//---
   datetime start=0;           // initial time set to 1970 year
   datetime end=TimeCurrent();  // the ending time set to the current server time
//--- request into the cache of the program the entire trading history
   HistorySelect(start,end);
//--- obtain the number of all of the orders in the history
   int history_orders=HistoryOrdersTotal();
//--- now scan through all of the orders
   for(int i=0;i<history_orders;i++)
     {
     //  processing each order in the history
     }   
 
    ...
       
//--- obtain the number of all deals in the history
   int deals=HistoryDealsTotal();
//--- now scan through all of the deals
   for(int i=0;i<deals;i++)
     {
     //  process each deal in the history
     }

大抵の場合、全トレーディング実績を処理しようとするのはまちがいです。処理済み取引/注文数が何千件、何万件に達すると、プログラムの動作は大幅に遅くなります。

注: 常に慎重に HistorySelect() 関数呼び出しの全件に注意を払うこと!思慮のない膨大な量のトレーディング履歴の mql5-program キャッシュへの格納はパフォーマンスを低下させます。

これは検証での一番重要な点です。ユーザーは、テスターが突然気が利くようになったと思い、クライアントターミナルでその理由を探し始めます。それで、まずMQL5プログラムのコードを最適化することを考えます(EAから呼び出されるEA とインジケーターに対して)。. コンピュータは鉄でできていて、多くのカーネルが内蔵されているという事実をあてにしないでください。

EAとオンラインインジケータが適切に動作するためにはこの点も重要です。プログラムのコードが最適化されていないと、超高性能コンピュータでも正しく動作するのは不可能です。

トレーディング履歴と連携するための正しいアルゴリズム

  1. キャッシュ内でトレーディング履歴をリクエストする必要性を判断します。必要なければ、以下の手順は省きます。
  2. トレーディング履歴の最終日を決定します。 (おそらくその時点までの履歴は必要ありません。)
  3. 最終日からさかのぼって、トレーディング履歴開始日を計算します。通常、EAでは一日、一週間程度が妥当でそれ以上長いトレーディング履歴は求められません。
  4. プロパティーを得るため、既知のチケットから取引と過去注文のチケットを取得します。
    • HistoryOrderGetDouble()
    • HistoryOrderGetInteger()
    • HistoryOrderGetString()
    • HistoryDealGetDouble()
    • HistoryDealGetInteger()
    • HistoryDealGetString()
  5. チケットがわからない場合、必要であればソートによってサイクルを編成します。
  6. ループ内で、インデックス(HistoryOrderGetTicket(Index)とHistoryDealGetTicket(Index))によりトレーディング履歴キャッシュから個別の取引/注文に対するチケットを取得します。
  7. 既知のチケットにより注文と取引について必要なプロパティを取得します(ポイント 4参照)。

本アルゴリズムのコード例

//--- the variable, which is set in true only during the change in the trading history
   bool TradeHistoryChanged=false;
//--- here we check for the changes in the history and put out the TradeHistoryChanged=true if needed
//... the needed code

//--- check  if there are changes in the trading history or not
   if(!TradeHistoryChanged) return;

//--- if the history has changed, then it makes sense to load it into the cache 
//--- the ending time set for the current server time
   datetime end=TimeCurrent();
//--- the beginning time is set to 3 days ago
   datetime start=end-3*PeriodSeconds(PERIOD_D1);
//--- request in the cache of the program, the trading history for the last 3 days
   HistorySelect(start,end);
//--- obtain the number of orders in the cache of the history
   int history_orders=HistoryOrdersTotal();
//--- now scan through the orders
   for(int i=0;i<history_orders;i++)
     {
      //--- obtain the ticket of the historical order
      ulong ticket=HistoryOrderGetTicket(i);
      //--- work with this order - receive its problems
      long order_magic=HistoryOrderGetInteger(ticket,ORDER_MAGIC);
      // obtain the rest of the properties for the order by the ticket
      // ...
     }

この例で示される基本的な考えは、 まず、トレード履歴において起こっている変更の事実を検証すべきであるということです選択肢の一つはOnTrade()関数内に真の値であるグローバル変数TradeHistoryChangedを設定 します。これは、トレード イベントは常にトレードイベントタイプを伴って返るためです。

トレード履歴に変更がなければ、トレード履歴をキャッシュに再度アップロードしてCPUのリソースを無駄に使用する必要はありません。論理的なので説明は必要ありませんね。トレード履歴に変更がある場合、必要な箇所のみアップロードし、個別の取引/注文は一度処理するだけですみます。不要な処理の繰り返しは避けます。

注:HistorySelect()関数により行われる全トレード履歴のキャッシュに対するリクエスト、および履歴からの取引や注文の処理サイクルにはしっかりした考え方が必要です。そうしないと、みなさんのコンピュータリソースは非効率的に使用されることになります。

トレード履歴との連携に関しては正しい例と誤った例を添付しています。各ファイル名は以下です。WrongWorkWithHistory.mq5 and RightWorkWithHistory.mq5.


履歴から注文ごとの情報を取得

過去注文に関する作業は現注文に関する作業となんら変わるところはありません。ただ一つ例外があります。mql5プログラムのキャッシュにある現注文数が2件以上でなければ、結果HistoryOrdersTotal()とキャッシュ内の過去注文数は、どれくらいのボリュームのトレード履歴がHistorySelect(start, end)、HistorySelectByPosition() または HistoryOrderSelection()関数によってロードされたかに依存します。

注:トレード履歴が関数HistorySelect()、HistorySelectByPosition()、またはHistoryOrderSelect()のいずれかによってmql5プログラムのキャッシュにロードされていない場合は、過去注文、取引と連携することはできません。トレード履歴データを受け取る前に、必ず必要な取引履歴と注文履歴をリクエストします。

たとえば、最終日の最終注文を検索し、情報が表示するスクリプトを提供します。

// --- determining the time intervals of the required trading history
   datetime end=TimeCurrent();                // current server time
   datetime start=end-PeriodSeconds(PERIOD_D1);// set the beginning for 24 hours ago
//--- request in the cache of the program the trading history for a day
   HistorySelect(start,end);
//--- receive the number of orders in the history
   int history_orders=HistoryOrdersTotal();
//--- obtain the ticket of the order, which has the last index in the list, from the history
   ulong order_ticket=HistoryOrderGetTicket(history_orders-1);
   if(order_ticket>0) // obtain in the cache the historical order, work with it
     {
      //--- order status
      ENUM_ORDER_STATE state=(ENUM_ORDER_STATE)HistoryOrderGetInteger(order_ticket,ORDER_STATE);
      long order_magic      =HistoryOrderGetInteger(order_ticket,ORDER_MAGIC);
      long pos_ID           =HistoryOrderGetInteger(order_ticket,ORDER_POSITION_ID);
      PrintFormat("Order #%d: ORDER_MAGIC=#%d, ORDER_STATE=%d, ORDER_POSITION_ID=%d",
                  order_ticket,order_magic,EnumToString(state),pos_ID);
     }
   else              // unsuccessful attempt to obtain the order
     {
      PrintFormat("In total, in the history of %d orders, we couldn't select the order"+
                  " with the index %d. Error %d",history_orders,history_orders-1,GetLastError());
     }

キャッシュからループ内の注文をソートし、その分析を求められるのが一般的です。一般的なアルゴリズムは以下に記されています。

  1. 履歴が HistorySelect() 関数によりロードされている場合、十分な 履歴の時間範囲を決定します。全体のトレード歴をキャッシュにロードすることは推奨されません。
  2. トレード履歴をプログラムのキャッシュにロードします。HistorySelect()、HistorySelectByPosition() 、HistoryOrderSelect (ticket)関数のいずれかです。
  3. HistoryOrdersTotal()を使用し、キャッシュ内の注文合計数を取得します。
  4. リスト上のインデックスにより全注文を検索することでサイクルを設定します。
  5. HistoryOrderGetTicket() 関数を用いてキャッシュ内の注文チケットを取得します。
  6. HistoryOrderGetDouble()、HistoryOrderGetInteger()、HistoryOrderGetString() 関数を使用し、キャッシュから注文データを取得します。必要であれば、取得されたデータを分析し、適切な処理を行います。

アルゴリズムの例:

#property script_show_inputs

input long my_magic=999;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
// --- setting the time intervals of the required trading history
   datetime end=TimeCurrent();                // the current server time
   datetime start=end-PeriodSeconds(PERIOD_D1);// set the beginning for 24 hours ago
//--- request into the cache of the program the needed interval of the trading history
   HistorySelect(start,end);
//--- obtain the number of orders in history
   int history_orders=HistoryOrdersTotal();
//--- now scroll through all of the orders
   for(int i=0;i<history_orders;i++)
     {
      //--- obtain the ticket of the order by its number in the list
      ulong order_ticket=HistoryOrderGetTicket(i);
      if(order_ticket>0) //  obtain in the cache, the historical order, and work with it
        {
         //--- time of execution
         datetime time_done=HistoryOrderGetInteger(order_ticket,ORDER_TIME_DONE);
         long order_magic  =HistoryOrderGetInteger(order_ticket,ORDER_MAGIC);
         long pos_ID       =HistoryOrderGetInteger(order_ticket,ORDER_POSITION_ID);
         if(order_magic==my_magic)
           {
           //  process the position with the set ORDER_MAGIC
           }
         PrintFormat("Order #%d: ORDER_MAGIC=#%d, time_done %s, ORDER_POSITION_ID=%d",
                     order_ticket,order_magic,TimeToString(time_done),pos_ID);
        }
      else               // unsuccessful attempt to obtain the order from the history
        {
         PrintFormat("we were not able to select the order with the index %d. Error %d",
                     i,GetLastError());
        }
     }
  }
注:常にHistorySelect() 関数の呼び出しに従うように注意してください!思慮のない膨大な量のトレーディング履歴の mql5-program キャッシュへの格納はパフォーマンスを低下させます。


履歴からの取引情報取得

取引の処理は、過去の注文データの処理と同じ特徴を持っています。トレード履歴内の取引数や、HisotryDealsTotal()関数の実行結果は、HisotrySelect(start,end))HistorySelectByPosition() 関数などにより格納されたトレード履歴データの量に左右されます。

HistoryDealSelect(ticket) 関数を使用し、取引データ一つのみをキャッシュに格納してください。

// --- determining the time intervals of the required trading history
   datetime end=TimeCurrent();                // current sever time
   datetime start=end-PeriodSeconds(PERIOD_D1);// set the beginning for 24 hours ago
//--- request in the cache of the program the needed interval of the trading history
   HistorySelect(start,end);
//--- obtain the number of deals in history
   int deals=HistoryDealsTotal();
//--- obtain the ticket for the deal, which has the last index in the list
   ulong deal_ticket=HistoryDealGetTicket(deals-1);
   if(deal_ticket>0) // we obtained in the cache of the deal, and work with it
     {
      //--- the ticket order, based on which the deal was made
      ulong order     =HistoryDealGetInteger(deal_ticket,DEAL_ORDER);
      long order_magic=HistoryDealGetInteger(deal_ticket,DEAL_MAGIC);
      long pos_ID     =HistoryDealGetInteger(deal_ticket,DEAL_POSITION_ID);
      PrintFormat("Deal #%d for the order #%d with the ORDER_MAGIC=%d  that participated in the position",
                  deals-1,order,order_magic,pos_ID);
     }
   else              // unsuccessful attempt of obtaining a deal
     {
      PrintFormat("In total, in the history %d of deals, we couldn't select a deal"+
                  " with the index %d. Error %d",deals,deals-1,GetLastError());
     }

一般的には、キャッシュから取引ループにて検索し、分析する必要があります。一般的なアルゴリズムは以下に記されています。

  1. 履歴がHistorySelect(start, end) 関数によってロードされている場合、十分な履歴境界を判断します。全ての履歴データをキャッシュに格納することは推奨されません。
  2. HIstorySelect()、HistorySelectByPosition() 関数によるトレーディング履歴をプログラムキャッシュにロードします。
  3. HistoryDealsTotal() 関数を用いて、履歴にある取引合計数を取得してください。
  4. リストの数字を用いて、全ての取引データを検索し、ループを作成します。
  5. HistoryDealGetTicket() 関数により、ャッシュの次の取引データのチケットを決定します。
  6. HistoryDealGetDouble()、HistoryDealGetInteger()、 HistoryDealGetString() 関数を使用し、キャッシュ内から取引情報を取得します。必要であれば、取得されたデータを分析し、適切な処理を行います。

損益計算のためのアルゴリズム例:

input long my_magic=111;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
// --- determine the time intervals of the required trading history
   datetime end=TimeCurrent();                 // current server time
   datetime start=end-PeriodSeconds(PERIOD_D1);// set the beginning time to 24 hours ago

//--- request in the cache of the program the needed interval of the trading history
   HistorySelect(start,end);
//--- obtain the number of deals in the history
   int deals=HistoryDealsTotal();

   int returns=0;
   double profit=0;
   double loss=0;
//--- scan through all of the deals in the history
   for(int i=0;i<deals;i++)
     {
      //--- obtain the ticket of the deals by its index in the list
      ulong deal_ticket=HistoryDealGetTicket(i);
      if(deal_ticket>0) // obtain into the cache the deal, and work with it
        {
         string symbol             =HistoryDealGetString(deal_ticket,DEAL_SYMBOL);
         datetime time             =HistoryDealGetInteger(deal_ticket,DEAL_TIME);
         ulong order               =HistoryDealGetInteger(deal_ticket,DEAL_ORDER);
         long order_magic          =HistoryDealGetInteger(deal_ticket,DEAL_MAGIC);
         long pos_ID               =HistoryDealGetInteger(deal_ticket,DEAL_POSITION_ID);
         ENUM_DEAL_ENTRY entry_type=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(deal_ticket,DEAL_ENTRY);

         //--- process the deals with the indicated DEAL_MAGIC
         if(order_magic==my_magic)
           {
            //... necessary actions
           }

         //--- calculate the losses and profits with a fixed results
         if(entry_type==DEAL_ENTRY_OUT)
          {
            //--- increase the number of deals 
            returns++;
            //--- result of fixation
            double result=HistoryDealGetDouble(deal_ticket,DEAL_PROFIT);
            //--- input the positive results into the summarized profit
            if(result>0) profit+=result;
            //--- input the negative results into the summarized losses
            if(result<0) loss+=result;
           }
        }
      else // unsuccessful attempt to obtain a deal
        {
         PrintFormat("We couldn't select a deal, with the index %d. Error %d",
                     i,GetLastError());
        }
     }
   //--- output the results of the calculations
   PrintFormat("The total number of %d deals with a financial result. Profit=%.2f , Loss= %.2f",
               returns,profit,loss);
  }
注:常にHistorySelect() 関数の呼び出しに従うように注意してください!思慮のない膨大な量のトレーディング履歴の mql5-program キャッシュへの格納はパフォーマンスを低下させます。


ポジションの識別子(POSITION_IDENTIFIER)による履歴のキャッシュ取得

HistorySelectByPosition (position_ID)関数は HistorySelect (start, end) 関数と同様、履歴から取引と注文データをキャッシュに格納します。 しかし、指定されたポジション識別子(POSITION_IDENTIFIER)を持つという条件下においてのみ有効です。ポジションの識別子は再度オープンされたポジションにそれぞれ自動で割り当てられるユニークな番号で、ライフタイム中変わることはありません。一方でポジションの変化(POSITION_TYPE_BUY からへPOSITION_TYPE_SELLのポジションタイプの変更)はポジションの識別子を変えることはないということを念頭に置く必要があります。

オープンポジションはすべて、そのインスツルメントの複数の取引の結果です。従って、ポジションの変化を分析するため、ライフタイム中、取引が行われる個々の取引を基に、取引と注文には、取引が行われるポジションの識別子が割り当てられます。なので、現在のオープンポジションの識別子を知ることで、変化を生んだ全ての注文、取引を発見するという全体の履歴を再構築することができるのです

HistorySelectByPosition(position_ID) 関数は、情報の検索のために全てのトレード履歴に対して反復処理するためにコードを書く必要性からプログラマーを解放する役目を果たします。この関数と連携する典型的なアルゴリズム:

  1. 正確なポジションIDを取得します。
  2. 関数 HistorySelectByPosition() によって、トエード履歴、現ポジションの識別子に等しい識別子を持つすべての注文と取引をトレード履歴キャッシュ内に取得します。
  3. アルゴリズムに従い、トレーディング履歴を処理します。


おわりに

全体のトレーディングサブシステムプラットフォームである MetaTrader 5は、考え抜かれており、ユーザーに優しく、さらにより効率的に個々の問題に取り組むことのできるような、豊富なトレーディング関数が備えられています。

しかし、標準的なライブラリーの特別なトレーディングクラスによりあまり多くの細部を気にすることなく高度なプログラムを書くことが可能となっているにもかかわらず、実装しようとしなくても、基本を理解することで、より信頼性のある効果的なトレーディングEAを作り上げることを可能にします。

全ての例は、この記事に添付されているファイルに記載されています。

MetaQuotes Software Corp.によりロシア語から翻訳された
元の記事: https://www.mql5.com/ru/articles/211

われわれはいかにして MetaTrader シグナルサービスとソーシャルトレーディングを発展させたのでしょうか われわれはいかにして MetaTrader シグナルサービスとソーシャルトレーディングを発展させたのでしょうか

われわれはシグナルサービスを強化し、メカニズムを改良し、新しい関数を追加し、欠陥を修正し続けています。2012年の MetaTrader シグナルサービスと現在の MetaTrader シグナルサービスはまったく異なる2つのサービスのようなものです。現在、特定バージョンの MetaTrader クライアントターミナルをサポートするサーバーのネットワークで構成される仮想ホスティングクラウドサービスを導入中です。

トレードシグナルを購読選択する場合のチップステップバイステップガイド トレードシグナルを購読選択する場合のチップステップバイステップガイド

このガイドは、シグナルサービス・トレードシグナルのテスト・要求されたシグナルを調べるアプローチシステムのためのものであり、潜在性・リスク・トレード意図・様々な口座タイプや金融ツールで稼働するという基準を満たします。

スピンドルチャートインジケーター スピンドルチャートインジケーター

この記事は、スピンドルチャート描写とトレード戦略とEAにおけるその使い方に関するものです。まず、日本のロウソク足チャートとも繋がりがある、そのチャートについてみていきます。次に、MQL5言語でのソースコードベースでのインジケーターの実装について分析します。インジケーターとトレード手法に基づいてEAをテストしてみましょう。

MQL5プログラミングベージックス:リスト MQL5プログラミングベージックス:リスト

トレーディング戦略開発のためのプログラミング言語の新バージョン、MQL5は、以前のバージョン、MQL4と比較してより強力で効果的な機能を提供しています。その利点は本質的にオブジェクト指向プログラミングの機能にあります。この記事は、ノードやリストなど複雑なカスタムデータ型を用いることについて詳しく見ていきます。また、MQL5での実際的なプログラミングにてリストを用いる例を紹介します。