English Русский Deutsch
preview
ニュース取引が簡単に(第5回):取引の実施(II)

ニュース取引が簡単に(第5回):取引の実施(II)

MetaTrader 5トレーディングシステム | 27 1月 2025, 09:29
139 0
Kabelo Frans Mampa
Kabelo Frans Mampa

はじめに

この記事の主な目的は、ニュース取引用のEAに逆指値注文(ストップ注文)を実装するためのコードを作成することです。これらの逆指値注文は、後続の記事においてニュースイベントを取引する際に使用されます。また、逆指値注文におけるスリッページを管理し、取引を終了させるための関数や、取引や注文が実行可能かどうかを検証するチェック機能も作成します。 取引管理は、アルゴリズム取引システムにおいて非常に重要な役割を果たします。具体的には、取引の開始と終了、ストップロス(損切り)の調整、テイクプロフィット(利食い)の管理といったタスクが含まれます。効率的な取引管理を行うことで、市場の不利な変動によるリスクを最小限に抑えつつ、トレーダーはより多くの利益を得ることが可能になります。


逆指値注文を使う理由


指値注文と逆指値注文

ニュースイベントを取引する際に逆指値注文を利用するのは一般的な戦略です。これは、主要な経済指標の発表後に起こりやすい急激な値動きを活用しながら、特定のリスクを最小限に抑えることができるためです。

ブレイクアウトの動きを捉える

経済指標の発表や中央銀行の声明などのニュースイベントは、突発的で大きな価格変動を引き起こすことが多く、これらはブレイクアウトとして知られています。逆指値注文を使用すると、価格が特定の閾値を超えた際にあらかじめ設定した価格で自動的に市場に参入することができるため、手動で相場を監視することなく、このようなブレイクアウトの動きを効果的に捉えることができます。

  • 買い逆指値注文:現在の市場価格よりも高い価格に設定します。価格が上昇した際に買い注文が発動し、ポジティブなニュースによる上昇モメンタムを捉えることができます。
  • 売り逆指値注文:現在の市場価格よりも低い価格に設定します。価格が下落した際に売り注文が発動し、ネガティブなニュースによる下落モメンタムを活用します。

市場のウィップソーを回避する可能性

ニュースイベントに伴う高いボラティリティは、明確な方向性が定まる前に価格が上下に大きく動くことがあります。このような状況で早すぎるタイミングでポジションを取ると、急激な値動き(ウィップソー)によりストップロスが発動してしまうことがあります。逆指値注文を活用すれば、市場が特定の価格レベルを超えて明確な方向性を示した場合にのみ取引が開始されるため、このようなフェイクアウトを回避するのに役立ちます。

過剰な分析を防ぐ

ニュースイベントを前にした取引は、相場の急激な動きによりストレスがたまりやすくなります。しかし、事前に逆指値注文を設定することで、トレーダーは感情に流されることなく、エントリーやエグジットを自動化できます。一度注文を設定すれば、意思決定は完了しているため、短期的な価格変動に過剰に反応してしまうリスクを避けられます。この記事の文脈では、例えばスピーチなど、事前に方向性を予測するのが難しいニュースイベントも、逆指値注文を用いることで取引できるということを意味します。

スリッページの最小化

逆指値注文を使用することで、特にボラティリティの高い時間帯に発生しがちな成行注文のスリッページを軽減できます。スリッページを完全に避けることはできないものの、逆指値注文を使うことで、ニュース発表後の大きな価格変動により遅れて発注された成行注文よりも、意図した価格に近いレベルで約定する可能性が高くなります。

リスク管理

逆指値注文はリスク管理を自動化する手段としても有効です。例えば、トレーダーがニュースイベントによる激しい値動きを予想する場合、買い逆指値注文と売り逆指値注文の両方を設定する(ストラドル戦略)ことができます。この方法により、価格がどちらに動いても適切に市場に参入することが可能になります。一方の注文が発動した場合には、もう一方の逆指値注文をキャンセルすることで、リスクを効率的に管理することができます。

実例

米ドルを対象に、最も市場を動かすニュースイベントの1つである非農業部門雇用者数(NFP)の発表を取引する場合を考えます。このデータがEUR/USDペアに大きな動きをもたらすと予想される場合、現在の価格より上に買い逆指値注文を、現在の価格より下に売り逆指値注文を設定することでストラドル戦略を実行します。この方法により、ニュース発表による急激な価格変動に柔軟に対応することができます。

NFP前のスナップショット

  • ポジティブなNFPデータ:ドル高が進行し、EUR/USDが下落して売り逆指値注文が発動します。
  • ネガティブなNFPデータ:ドル安が進行し、EUR/USDが上昇して買い逆指値注文が発動します。

NFP後のスナップショット

逆指値注文を利用することで、相場がどちらに動く場合でも事前に備えることができ、価格の動きによってブレイクアウトの方向性が確認されると、自動的にマーケットに参入することが可能になります。


口座プロパティクラス

CAccountPropertiesクラスは、MQL5のCAccountInfoクラスを継承したクラスです。このクラスの目的は、CAccountInfoにカスタムメソッドを追加し、取引口座内の特定の注文(例:売買指値注文や逆指値注文)の総数を取得する機能を提供することです。この関数は取引管理クラスで活用され、EAが口座の注文制限を超えないようにするために役立ちます。

例えば、口座の指値注文制限が200の場合、ユーザーの口座では、現在の注文(オープンおよび保留中の注文を含む)が200件を超えることはできません。この状況で、買い逆指値注文と売り逆指値注文を発注しようとしても、すでに199件の注文が存在している場合、EAは、両方の注文を発注するのに十分な余裕がないと判断します。この場合、ユーザーの口座には新たな注文は追加されません。

ヘッダーとインクルード

以下のコードには、MQL5標準ライブラリ内のAccountInfo.mqhファイルが含まれています。このファイルには、取引口座情報にアクセスするための定義済みの関数や構造体が含まれています。

#include <Trade/AccountInfo.mqh>

CAccountPropertiesクラス定義

継承:CAccountPropertiesクラスは、CAccountInfoを継承しています。継承することで、CAccountPropertiesは、CAccountInfoの関数とデータメンバーにアクセスできるようになります。

CAccountInfoは、残高、エクイティ、余剰証拠金などの重要な口座データを提供します。

クラスの目的:CAccountPropertiesクラスは、特定のタイプの注文を数えるメソッド(numOrders)を追加します。

class CAccountProperties: public CAccountInfo

numOrders関数

numOrders()関数は、ほとんどの動作がおこなわれる場所です。この関数は、取引口座で現在開いている特定の注文タイプ(指値注文と逆指値注文)の数をカウントします。

  • 戻り型:この関数は整数を返します。
  • 初期化:変数numは0に初期化されます。これは、希望するタイプに一致する注文の数を保存します。

int CAccountProperties::numOrders(void)
{
   int num=0;

注文の反復処理

  • OrdersTotal()は、注文の合計数を返すMQL5関数です。
  • ループを使用して、インデックス0からOrdersTotal() - 1までのすべての注文を繰り返し処理します。

for(int i=0; i<OrdersTotal(); i++)

注文の検証

  • OrderGetTicket(i)は、インデックスiの注文のチケット番号を取得するMQL5関数です。各注文には固有のチケット番号があります。
  • チケット番号が0より大きければ、注文が有効であることを意味し、コードはそのタイプの確認に進みます。

if(OrderGetTicket(i)>0)

注文タイプの確認

  • OrderGetInteger(ORDER_TYPE)は、インデックスiの注文のタイプを取得します。この値は、様々なタイプの注文(買い指値、売り逆指値など)を表すintに対応します。
  • switch文は、各注文のタイプを確認し、定義済みの注文タイプ定数と比較します。
switch(int(OrderGetInteger(ORDER_TYPE)))

特定の注文タイプの処理

  • 注文タイプ
    • ORDER_TYPE_BUY_LIMIT:買い指値注文
    • ORDER_TYPE_BUY_STOP:買い逆指値注文
    • ORDER_TYPE_BUY_STOP_LIMIT:買いストップリミット注文
    • ORDER_TYPE_SELL_LIMIT:売り指値注文
    • ORDER_TYPE_SELL_STOP:売り逆指値注文
    • ORDER_TYPE_SELL_STOP_LIMIT:売りストップリミット注文

認識された注文タイプごとに、カウンタ番号がインクリメントされます。注文が他のタイプである場合、デフォルトのケースが発動され、アクションは取られません。

case ORDER_TYPE_BUY_LIMIT:
    num++;
    break;
case ORDER_TYPE_BUY_STOP:
    num++;
    break;
case ORDER_TYPE_BUY_STOP_LIMIT:
    num++;
    break;
case ORDER_TYPE_SELL_LIMIT:
    num++;
    break;
case ORDER_TYPE_SELL_STOP:
    num++;
    break;
case ORDER_TYPE_SELL_STOP_LIMIT:
    num++;
    break;
default:
    break;

カウントを返す

  • すべての注文を反復処理し、有効な注文をカウントした後、この関数はnumに格納されている合計カウントを返します。

   return num;
}



リスク管理クラス

このクラスは、トレーダーが市場リスクへのエクスポージャーを適切に制御し、潜在的な損失を制限しながらも利益を得るための余地を確保することを目的としています。このクラスには、小規模な更新が加えられています。 以下のコードでは、注文の分類を表す列挙型OrderTypeSelectionが定義されています。この列挙型により、ユーザーは取引に使用したい注文タイプを選択することができます。
//-- Enumeration for Order type
enum OrderTypeSelection
  {
   MarketPositionType,//MARKET POSITION
   StopOrdersType,//STOP ORDERS
   StopOrderType,//SINGLE STOP ORDER
  } myOrderSelection;

列挙体:OrderTypeSelection

OrderTypeSelection列挙型は3つの異なる値で構成されます。

MarketPositionType

  • 成行注文を表します。
  • このタイプは市場の現在値に基づいて出され、現在値で即座に約定します。
  • イベントへの影響が必要です。

StopOrdersType

  • 逆指値注文を表します。
  • 逆指値注文は、価格が一定のレベルに達したときにのみ執行される条件付き注文です。買い逆指値注文と売り逆指値注文の両方が発注されます。
  • イベントに影響を与える必要はありません。

StopOrderType

  • 単一の逆指値注文を表します。
  • 個別の買い逆指値注文または売り逆指値注文が発注されます。
  • イベントへの影響が必要です。

変数宣言:myOrderSelection

  • myOrderSelectionは、トレーダーが現在選択している注文タイプを格納する変数です。

ロットサイズの正規化

以下の関数では、OrderTypeSelectionに応じて数量制限が変わります。myOrderSelectionがStopOrdersType(STOP ORDERS)の場合、買い逆指値注文と売り逆指値注文の両方を同時に開く必要があるため、数量制限は半分になります。一方、MarketPositionType(MARKET POSITION)とStopOrderType(SINGLE STOP ORDER)では、単一の注文のみが開きます。

void CRiskManagement::NormalizeLotsize(double &Lotsize)
{
    // Adjust lot size to match symbol's step size or minimum size
    if (Lotsize <= 0.0) return;
    double VolumeLimit = (myOrderSelection != StopOrdersType) ? CSymbol.LotsLimit() : CSymbol.LotsLimit() / 2;
    // Check and adjust if volume exceeds limits
    // ...
}



セッションクラス

以下のコードでは、CSessionsクラスを定義しています。このクラスは、特定の取引銘柄における取引セッションの時間を管理し、追跡する役割を担っています。このクラスは、基本クラスCTimeManagementのメソッド(Timemanagement.mqhを介して含まれる)を使用して、取引セッションが開始したかどうか、終了したかどうか、セッション終了時刻を取得します。この情報を活用することで、事前に取引を終了し、注文の有効期限を設定することが可能になります。これにより、オーバーナイト取引を回避することが目的です。

なぜオーバーナイト取引を避けるのでしょうか。

オーバーナイト取引

ボラティリティによる市場リスクの増大

市場は、経済イベントや地政学的な動向、他のグローバル市場からのニュースに敏感に反応し、一晩で大きく変動することがあります。特に、大きなイベントが通常の取引時間外に発生する場合、夜間に保有するポジションは予測不可能な値動きに直面するリスクがあります。以下のような要因がその原因となる場合があります。

  • 翌日の市場開始時に価格が大きく変動し、予想外の利益や損失が発生する可能性がある。
  • 流動性が低下することで、ポジションの変更や決済が迅速におこなえず、急激な市場変化への対応が困難になる可能性がある

限られた流動性

オーバーナイト取引では市場参加者が少ないため、流動性が低下しやすい状況です。その結果、以下のような影響が生じる可能性があります。

  • 買値と売値のスプレッドが拡大し、取引コストが上昇する
  • スリッページが発生しやすくなり、市場の深さが不足しているため予想とは異なる価格で取引が成立する
  • 大量のポジションを市場に影響を与えずに決済することが困難となり、不利な価格での執行につながる可能性がある

ギャップのリスク増大

オーバーナイト取引では、市場オープン時の価格が前日の終値と大きく異なるプライスギャップが発生し、トレーダーが予期せぬリスクにさらされる可能性があります。このギャップは、ニュースリリースやその他の出来事によって引き起こされることがあります。主な影響は以下の通りです。

  • 価格がポジションに不利な方向に動いた場合、大きな損失を被るリスクがある
  • ギャップにより設定したストップレベルを飛び越え、予想以上に不利な価格で注文が執行される可能性があるため、ストップロス注文が機能しない場合がある

金利手数料(スワップ料)

FXなど特定の商品では、ポジションを夜間保有することで、関係する通貨間の金利差に基づきスワップ料や金利手数料が発生する場合があります。これらのコストは長期間にわたって蓄積され、収益性を圧迫する可能性があります。主な影響は以下の通りです。

  • 長期間保有することで、利益が出ている取引がスワップ料の蓄積により損失に転じる場合がある
  • スワップ料は市場状況によって変動するため、予測や計画が難しくなる

取引執行に対するコントロールの低下

夜間、特に取引時間が限られていたり不規則な市場では、取引の執行が遅くなったり、精度が低下したりすることがあります。

  • ブローカーの営業時間:ブローカーによっては、通常の営業時間外での特定の取引操作や変更をサポートしていない場合があり、市場の状況に対応する能力が低下する可能性がある

//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/ja/users/kaaiblo |
//+------------------------------------------------------------------+
#include "Timemanagement.mqh"
//+------------------------------------------------------------------+
//|Sessions Class                                                    |
//+------------------------------------------------------------------+
class CSessions:CTimeManagement
  {
public:
                     CSessions(void) {}
                    ~CSessions(void) {}
   //--- Check if trading Session has began
   bool              isSessionStart(int offsethour=0,int offsetmin=0);
   //--- Check if trading Session has ended
   bool              isSessionEnd(int offsethour=0,int offsetmin=45);
   //--- Get Session End datetime
   datetime          SessionEnd(int offsethour=0,int offsetmin=45);
  };
//+------------------------------------------------------------------+
//|Check if trading Session has started                              |
//+------------------------------------------------------------------+
bool CSessions::isSessionStart(int offsethour=0,int offsetmin=0)
  {
//--- Declarations
   datetime datefrom,dateto,DateFrom[],DateTo[];

//--- Find all session times
   for(int i=0; i<10; i++)
     {
      //--- Get the session dates for the current symbol and Day of week
      if(SymbolInfoSessionTrade(Symbol(),DayOfWeek(TimeTradeServer()),i,datefrom,dateto))
        {
         //--- Check if the end date's hour is at midnight
         if(ReturnHour(dateto)==00||ReturnHour(dateto)==24)
           {
            //--- Adjust the date to one minute before midnight
            dateto = Time(TimeTradeServer(),23,59);
           }
         //--- Re-adjust DateFrom Array size
         ArrayResize(DateFrom,int(ArraySize(DateFrom))+1,int(ArraySize(DateFrom))+2);
         //--- Assign the last array index datefrom value
         DateFrom[int(ArraySize(DateFrom))-1] = datefrom;
         //--- Re-adjust DateTo Array size
         ArrayResize(DateTo,int(ArraySize(DateTo))+1,int(ArraySize(DateTo))+2);
         //--- Assign the last array index dateto value
         DateTo[int(ArraySize(DateTo))-1] = dateto;
        }
     }

//--- Check if there are session times
   if(DateFrom.Size()>0)
     {
      /* Adjust DateFrom index zero date as the first index date will be the earliest date
       from the whole array, we add the offset to this date only*/
      DateFrom[0] = TimePlusOffset(DateFrom[0],MinutesS(offsetmin));
      DateFrom[0] = TimePlusOffset(DateFrom[0],HoursS(offsethour));
      //--- Iterate through the whole array
      for(uint i=0; i<DateFrom.Size(); i++)
        {
         //--- Check if the current time is within the trading session
         if(TimeIsInRange(Time(Today(ReturnHour(DateFrom[i]),ReturnMinute(DateFrom[i])))
                          ,Time(Today(ReturnHour(DateTo[i]),ReturnMinute(DateTo[i])))))
           {
            return true;
           }
        }
     }
   else
     {
      //--- If there are no trading session times
      return true;
     }
   return false;
  }

//+------------------------------------------------------------------+
//|Check if trading Session has ended                                |
//+------------------------------------------------------------------+
bool CSessions::isSessionEnd(int offsethour=0,int offsetmin=45)
  {
//--- Declarations
   datetime datefrom,dateto,DateTo[],lastdate=0,sessionend;

//--- Find all session times
   for(int i=0; i<10; i++)
     {
      //--- Get the session dates for the current symbol and Day of week
      if(SymbolInfoSessionTrade(Symbol(),DayOfWeek(TimeTradeServer()),i,datefrom,dateto))
        {
         //--- Check if the end date's hour is at midnight
         if(ReturnHour(dateto)==00||ReturnHour(dateto)==24)
           {
            //--- Adjust the date to one minute before midnight
            dateto = Time(TimeTradeServer(),23,59);
           }
         //--- Re-adjust DateTo Array size
         ArrayResize(DateTo,int(ArraySize(DateTo))+1,int(ArraySize(DateTo))+2);
         //--- Assign the last array index dateto value
         DateTo[int(ArraySize(DateTo))-1] = dateto;
        }
     }

//--- Check if there are session times
   if(DateTo.Size()>0)
     {
      //--- Assign lastdate a default value
      lastdate = DateTo[0];
      //--- Iterate through the whole array
      for(uint i=0; i<DateTo.Size(); i++)
        {
         //--- Check for the latest date in the array
         if(DateTo[i]>lastdate)
           {
            lastdate = DateTo[i];
           }
        }
     }
   else
     {
      //--- If there are no trading session times
      return false;
     }
   /* get the current time and modify the hour and minute time to the lastdate variable
   and assign the new datetime to sessionend variable*/
   sessionend =  Time(Today(ReturnHour(lastdate),ReturnMinute(lastdate)));
//--- Re-adjust the sessionend dates with the minute and hour offsets
   sessionend = TimeMinusOffset(sessionend,MinutesS(offsetmin));
   sessionend = TimeMinusOffset(sessionend,HoursS(offsethour));

//--- Check if sessionend date is more than the current time
   if(TimeTradeServer()<sessionend)
     {
      return false;
     }
   return true;
  }

//+------------------------------------------------------------------+
//|Get Session End datetime                                          |
//+------------------------------------------------------------------+
datetime CSessions::SessionEnd(int offsethour=0,int offsetmin=45)
  {
//--- Declarations
   datetime datefrom,dateto,DateTo[],lastdate=0,sessionend;

//--- Find all session times
   for(int i=0; i<10; i++)
     {
      //--- Get the session dates for the current symbol and Day of week
      if(SymbolInfoSessionTrade(Symbol(),DayOfWeek(TimeTradeServer()),i,datefrom,dateto))
        {
         //--- Check if the end date's hour is at midnight
         if(ReturnHour(dateto)==00||ReturnHour(dateto)==24)
           {
            //--- Adjust the date to one minute before midnight
            dateto = Time(TimeTradeServer(),23,59);
           }
         //--- Re-adjust DateTo Array size
         ArrayResize(DateTo,int(ArraySize(DateTo))+1,int(ArraySize(DateTo))+2);
         //--- Assign the last array index dateto value
         DateTo[int(ArraySize(DateTo))-1] = dateto;
        }
     }

//--- Check if there are session times
   if(DateTo.Size()>0)
     {
      //--- Assign lastdate a default value
      lastdate = DateTo[0];
      //--- Iterate through the whole array
      for(uint i=0; i<DateTo.Size(); i++)
        {
         //--- Check for the latest date in the array
         if(DateTo[i]>lastdate)
           {
            lastdate = DateTo[i];
           }
        }
     }
   else
     {
      //--- If there are no trading session times
      return 0;
     }
   /* get the current time and modify the hour and minute time to the lastdate variable
   and assign the new datetime to sessionend variable*/
   sessionend = Time(Today(ReturnHour(lastdate),ReturnMinute(lastdate)));
//--- Re-adjust the sessionend dates with the minute and hour offsets
   sessionend = TimeMinusOffset(sessionend,MinutesS(offsetmin));
   sessionend = TimeMinusOffset(sessionend,HoursS(offsethour));
//--- return sessionend date
   return sessionend;
  }
//+------------------------------------------------------------------+

主要コンポーネントの説明

クラスの定義:CSessions

  • 継承
    • CSessionsクラスはCTimeManagementを継承しているので、その基底クラスで定義されている時間管理関連のメソッドや属性にアクセスできます。
  • メソッド
    • isSessionStart(int offsethour=0, int offsetmin=0)
      • 現在のサーバー時刻とセッション開始時刻を照合し、取引セッションが開始されたかどうかを判断します。また、オプションで時間オフセットも可能です。
    • isSessionEnd(int offsethour=0、int offsetmin=45)
      • 現在のサーバー時刻とセッション終了時刻を照合し、取引セッションが終了したかどうかを判定します。isSessionStartと同様に、オプションのオフセットを取ります(デフォルト:45分)。
    • SessionEnd(int offsethour=0, int offsetmin=45)
      • 現在の取引セッションの終了日時を、指定したオフセットで調整した上で返します(デフォルト:45分)。

メソッドの詳細

isSessionStart(int offsethour=0, int offsetmin=0)

このメソッドは、サーバー時間と取引銘柄のセッションスケジュールに基づいて、取引セッションが開始されたかどうかを確認します。

変数

  • datfrom、dateto:各取引日のセッション開始時刻と終了時刻を保存するために使用されます。
  • DateFrom[]、DateTo[]:調整後のセッション開始時刻と終了時刻を保持する配列

ロジック

  • セッション検索
    • SymbolInfoSessionTrade関数は、現在の銘柄と曜日(DayOfWeek(TimeTradeServer())で取得)の取引セッションの開始時刻(datefrom)と終了時刻(dateeto)を取得します。
    • セッション終了時刻(dateto)が午前0時(00:00または24:00)の場合、時刻は午前0時の1分前(23:59)に調整されます。
  • 配列の操作
    • セッションの開始時刻(datefrom)はDateFrom[]配列に、終了時刻(dateto)はDateTo[]配列に格納されます。
  • オフセットの適用
    • このメソッドは、ヘルパー関数MinutesS()およびHoursS()を使用して、指定されたoffsethourおよびoffsetminによって最初のセッションの開始時刻を調整します。
  • 現在時刻の確認
    • 次に、このメソッドはDateFrom[]とDateTo[]のセッション時間を繰り返し、現在のサーバー時間(TimeTradeServer())がセッション間隔のいずれかに含まれるかどうかを確認します。
    • 有効なセッションが見つかればtrueを返し、セッションが開始されたことを示します。そうでない場合はfalseを返します。

isSessionEnd(int offsethour=0, int offsetmin=45)

このメソッドは、取引セッションが終了したかどうかを確認します。

変数

  • isSessionStart()と似ていますが、終了時刻(dateto)に注目しています。
  • lastdate:比較する最新のセッション終了時刻を格納します。
  • sessionend:オフセットを適用して最終的に計算されたセッション終了時刻を保持します。

ロジック

  • セッション検索
    • isSessionStart()と同様に、セッション時間(datefrom、dateto)を取得します。
    • DateTo[]に有効なセッション時刻があるかどうかを調べ、最新のセッション終了時刻(lastdate)を特定します。
  • オフセットの適用
    • オフセット(offsethourとoffsetmin)を適用してセッション終了時刻を調整します。
  • サーバー時間との比較
    • 現在時刻が調整されたセッション終了時刻より前であれば、falseを返し、セッションがまだ進行中であることを示します。セッションが終了した場合(現在時刻がsessionend以降)、trueを返します。

SessionEnd(int offsethour=0, int offsetmin=45)

このメソッドは、指定された時間オフセットを適用して、現在のセッションの終了日時の値を返します。

ロジック

  • isSessionEnd()と同様に、セッション時間を取得し、オフセットを適用して、最終的に調整されたセッション終了時間(sessionend)を返します。
オーバーナイト取引の短所のまとめ
  • ボラティリティの増大と予測不可能な市場の動き
  • 流動性が低いため、スプレッドが拡大し、コストが上昇する
  • 価格ギャップのリスクがあり、大きな損失につながる可能性がある
  • 世界的な出来事とその市場への影響に触れる
  • 外国為替市場における金利手数料またはスワップ手数料
  • 取引の執行と意思決定に対するコントロールの低下


取引管理クラス

CTradeManagementクラスはCRiskManagementを継承し、売買注文の発注、ストップロスとテイクプロフィットのレベルの処理、逆指値注文の管理など、取引を管理するための機能を含んでいます。

売買注文のフローチャート

+---------------------------+
| Receive Trade Signal      |
+---------------------------+
              |
              V
+---------------------------+
| Check for Available Margin|
+---------------------------+
              |
              V
+---------------------------+
| Place Buy/Sell Order      |
+---------------------------+
              |
              V
+----------------------------+
| Set Stop Loss & Take Profit|
+----------------------------+
              |
              V
+----------------------------+
| Monitor Open Position      |
+----------------------------+
              |
        If conditions met:
              |
              V
+----------------------------+
| Adjust SL/TP or Close Trade|
+----------------------------+
//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/ja/users/kaaiblo |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
#include <Trade\OrderInfo.mqh>
#include <Trade\SymbolInfo.mqh>
#include "RiskManagement.mqh"
#include "TimeManagement.mqh"
#include "Sessions.mqh"

//+------------------------------------------------------------------+
//|TradeManagement class                                             |
//+------------------------------------------------------------------+
class CTradeManagement:CRiskManagement
  {
private:
   CTrade            Trade;//Trade class object
   CSymbolProperties CSymbol;//SymbolProperties class object
   CTimeManagement   CTime;//TimeManagement class object
   CSessions         CTS;//Sessions class object
   bool              TradeResult;//boolean to store trade result
   double            mySL;//double variable to store Stoploss
   double            myTP;//double variable to store Takeprofit
   uint              myDeviation;//store price deviation for stop orders
   double            myOpenPrice;//store open price for stop orders
   //--- Will retrieve if there are any open trades
   bool              OpenTrade(ENUM_POSITION_TYPE Type,ulong Magic,string COMMENT=NULL);
   //--- Will retrieve if there are any deals
   bool              OpenedDeal(ENUM_DEAL_TYPE Type,ulong Magic,string COMMENT=NULL);
   //--- Will retrieve if there are any open orders
   bool              OpenOrder(ENUM_ORDER_TYPE Type,ulong Magic,string COMMENT=NULL);
   //--  Check if trade is valid
   bool              Valid_Trade(ENUM_POSITION_TYPE Type,double Price,double SL,double TP);
   //-- Check if stop order is valid
   bool              Valid_Order(ENUM_ORDER_TYPE Type,double Price,double SL,double TP);
   //--- Will attempt to open buy trade
   bool              Buy(double SL,double TP,ulong Magic,string COMMENT=NULL);
   //--- Will attempt to open sell trade
   bool              Sell(double SL,double TP,ulong Magic,string COMMENT=NULL);

   //--- class to set and retrieve an order's properties
   class OrderSettings
     {
   private:
      struct TradeProperties
        {
         //store open-price,take-profit,stop-loss for stop orders
         double         Open,Take,Stop;
        } myTradeProp;
   public:
                     OrderSettings() {}
      //--- Set order properties
      void           Set(double myOpen,double myTake,double myStop)
        {
         //--- Set open-price
         myTradeProp.Open=myOpen;
         //--- Set take-profit
         myTradeProp.Take=myTake;
         //--- Set stop-loss
         myTradeProp.Stop=myStop;
        }
      TradeProperties Get()
        {
         //--- retrieve order properties
         return myTradeProp;
        }
     };

   //--- Declare variables for different order types
   OrderSettings     myBuyStop,mySellStop,myBuyTrade,mySellTrade;

   //--- Will set buy-stop order properties
   void              SetBuyStop(int SL,int TP);
   //--- Will set buy position properties
   void              SetBuyTrade(int SL,int TP,double OP);
   //--- Will set sell-stop order properties
   void              SetSellStop(int SL,int TP);
   //--- Will set sell position properties
   void              SetSellTrade(int SL,int TP,double OP);

public:
   //--- Class constructor
                     CTradeManagement(uint deviation,string SYMBOL=NULL)
                     :myDeviation(deviation)//Assign deviation value
     {
      //--- Set symbol name
      CSymbol.SetSymbolName(SYMBOL);
     }
   //--- Class destructor
                    ~CTradeManagement(void)
     {
     }
   //--- Will attempt to open buy trade
   bool              Buy(int SL,int TP,ulong Magic,string COMMENT=NULL);
   //--- Will attempt to open sell trade
   bool              Sell(int SL,int TP,ulong Magic,string COMMENT=NULL);
   /*This function will delete a pending order if the previous opposing pending order is
   opened into a position, this function is used when trading with StopOrdersType(STOP ORDERS)*/
   void              FundamentalMode(string COMMENT_COMMON);
   /* Function will attempt to re-adjust stop-losses or take-profit values that have
   been changed due to slippage on an order when opening.
   */
   void              SlippageReduction(int SL,int TP,string COMMENT_COMMON);
   //--- This function will open both buy-stop and sell-stop orders for StopOrdersType(STOP ORDERS)
   bool              OpenStops(int SL,int TP,ulong Magic,string COMMENT=NULL);
   //--- Will attempt to open a sell-stop order
   bool              OpenSellStop(int SL,int TP,ulong Magic,string COMMENT=NULL);
   //--- Will attempt to open a buy-stop order
   bool              OpenBuyStop(int SL,int TP,ulong Magic,string COMMENT=NULL);
   //--- Function will attempt to close all trades depending on the position comment
   void              CloseTrades(string COMMENT_COMMON);
  };
// ...

Privateメンバー

  • オブジェクトと変数
    • CTrade Trade:取引執行(売買注文)等を管理する
    • CSymbolProperties CSymbol:銘柄のプロパティを管理する
    • CTimeManagement CTime:時間関連のロジックを処理する
    • CSessionsのCTS:取引時間に関連するセッションを管理する
    • bool TradeResult:取引の結果(成功/失敗)を示すブーリアンフラグ
    • double mySL:ストップロス値
    • double myTP:テイクプロフィット値
    • uint myDeviation;:逆指値注文の価格偏差
    • double myOpenPrice:逆指値注文の建値を保存する

OrderSettings内部クラス

class OrderSettings
{
private:
   struct TradeProperties
   {
      double Open, Take, Stop;
   } myTradeProp;
public:
   OrderSettings() {}
   void Set(double myOpen, double myTake, double myStop)
   {
      myTradeProp.Open = myOpen;
      myTradeProp.Take = myTake;
      myTradeProp.Stop = myStop;
   }
   TradeProperties Get()
   {
      return myTradeProp;
   }
};

  • 目的:この内部クラスは、取引のプロパティ(建値、テイクプロフィット、ストップロス)をカプセル化します。これらのプロパティを簡単に設定取得できます。
    • myTradeProp:取引プロパティを保有するための構造体
    • Set():建値、テイクプロフィット値、ストップロス値を設定するメソッド
    • Get():保存されている取引プロパティを取得するメソッド
  • OrderSettingsを使用した変数

OrderSettings myBuyStop, mySellStop, myBuyTrade, mySellTrade;

    • これらのオブジェクトは、さまざまな注文タイプ(buy-stop、sell-stop、buy-trade、sell-trade)のプロパティを格納します

以下のコードにあるFundamentalMode関数は、反対注文が約定してポジションとして建てられたときに、未決済の逆指値注文を削除するように設計されています。この機能は、買い逆指値注文と売り逆指値注文の両方を使用する戦略(ストラドル戦略など)に関連し、ボラティリティの高い市場でポジションをエントリーします。逆指値注文の1つが発動され、ポジションが建てられると、残りの反対注文(現在は冗長)は不要な取引を避けるために削除されます。

例:

Buy-stopが1.13118に、Sell-stopが1.12911にオープンされ、Buy-stopが最初に実行され、操作ログメッセージ「deal performed [#2 buy 0.01 EURUSD at 1.13134]」が表示されます。したがって、この場合、この関数は、残りの売り注文を削除/キャンセルし、操作ログメッセージ「order canceled [#3 sell stop 0.01 EURUSD at 1.12911]」を表示します。 

FundamentalModeによる注文キャンセル

//+------------------------------------------------------------------+
//|This function will delete a pending order if the previous opposing|
//|pending order is opened into a position, this function is used    |
//|when trading with StopOrdersType(STOP ORDERS)                     |
//+------------------------------------------------------------------+
void CTradeManagement::FundamentalMode(string COMMENT_COMMON)
  {
//--- Iterate through all open positions if Orders are more than zero
   for(int P=0; P<PositionsTotal()&&OrdersTotal()>0; P++)
     {
      //--- Check if Position ticket is above zero
      if(PositionGetTicket(P)>0)
        {
         //--- Check if the Position's Symbol,Magic,Type,Comment is correct
         if(PositionGetString(POSITION_SYMBOL)==CSymbol.GetSymbolName()&&
            StringFind(PositionGetString(POSITION_COMMENT),COMMENT_COMMON)>=0)
           {
            //--- Iterate through all open orders
            for(int O=0; O<OrdersTotal(); O++)
              {
               //--- Check if Order ticket is above zero
               if(OrderGetTicket(O)>0)
                 {
                  //--- Check if the Order's Symbol,Magic,Comment is correct
                  if(OrderGetString(ORDER_SYMBOL)==CSymbol.GetSymbolName()
                     &&OrderGetInteger(ORDER_MAGIC)==PositionGetInteger(POSITION_MAGIC)
                     &&StringFind(OrderGetString(ORDER_COMMENT),COMMENT_COMMON)>=0
                     &&OrderGetString(ORDER_COMMENT)==PositionGetString(POSITION_COMMENT))
                    {
                     //--- Identify Position type
                     switch(int(PositionGetInteger(POSITION_TYPE)))
                       {
                        /* In the case that the Position type is a buy and if the corresponding order type is
                        a sell-stop then delete this order*/
                        case  POSITION_TYPE_BUY:
                           if(OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP)
                             {
                              //--- Delete the sell-stop order
                              Trade.OrderDelete(OrderGetTicket(O));
                             }
                           break;
                        /* In the case that the Position type is a sell and if the corresponding order type is
                        a buy-stop then delete this order*/
                        case POSITION_TYPE_SELL:
                           if(OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP)
                             {
                              //--- Delete the sell-stop order
                              Trade.OrderDelete(OrderGetTicket(O));
                             }
                           break;
                        default:
                           break;
                       }
                    }
                 }
              }
           }
        }
     }
  }

関数の目的

この関数は、未決注文(例えば、買い逆指値注文や売り逆指値注文)が取引としてオープンされると、反対注文(例えば、売り逆指値注文や買い逆指値注文)が削除されることを保証します。これにより、両方の注文が実行され、望ましくないポジションが建てられることを防ぐことができます。

void CTradeManagement::FundamentalMode(string COMMENT_COMMON)

  • FundamentalMode:この関数は、反対側のポジションが建てられた場合、未決済の逆指値注文を削除しようとします。
  • COMMENT_COMMON:取引/注文に関連するコメントに基づいて取引/注文を識別するために使用される文字列パラメータ

ポジションの反復処理

for(int P = 0; P < PositionsTotal() && OrdersTotal() > 0; P++)

  • PositionsTotal():ポジションの総数を返します。
  • OrdersTotal():未決注文の総数を返します。
  • このforループは、すべての未決済ポジションを繰り返し処理し、処理すべき未決注文があることを確認します。

ポジションチケットの検証

if(PositionGetTicket(P) > 0)

  • PositionGetTicket(P):インデックスPのポジションのチケット番号を取得します。
  • ポジションチケットが有効(つまりゼロ以上)であることを確認します。

ポジションプロパティの確認

if(PositionGetString(POSITION_SYMBOL) == CSymbol.GetSymbolName() &&
   StringFind(PositionGetString(POSITION_COMMENT), COMMENT_COMMON) >= 0)

  • PositionGetString(POSITION_SYMBOL):ポジションの銘柄を取得します。
  • CSymbol.GetSymbolName():コンストラクタで設定した銘柄の銘柄名を取得します。
  • PositionGetString(POSITION_COMMENT):ポジションに関連するコメントを取得します。
  • StringFind(PositionGetString(POSITION_COMMENT), COMMENT_COMMON):COMMENT_COMMON部分文字列がポジションのコメントに存在するかどうかを確認します。ポジションが戦略/EAのものであることを保証します。

すべての未決注文の反復処理

for(int O = 0; O < OrdersTotal(); O++)

  • すべての未決注文を繰り返します。

注文チケットの検証

if(OrderGetTicket(O) > 0)

  • OrderGetTicket(O):インデックスOの注文のチケット番号を取得します。
  • 注文チケットが有効であることを確認します。

注文プロパティのチェック

if(OrderGetString(ORDER_SYMBOL) == CSymbol.GetSymbolName() &&
   OrderGetInteger(ORDER_MAGIC) == PositionGetInteger(POSITION_MAGIC) &&
   StringFind(OrderGetString(ORDER_COMMENT), COMMENT_COMMON) >= 0 &&
   OrderGetString(ORDER_COMMENT) == PositionGetString(POSITION_COMMENT))

  • OrderGetString(ORDER_SYMBOL):注文に関連する銘柄を取得します。
  • OrderGetInteger(ORDER_MAGIC):注文のマジックナンバーを取得します(各イベントの注文を識別するのに役立ちます)。
  • PositionGetInteger(POSITION_MAGIC):ポジションのマジックナンバーを取得し、注文のマジックナンバーと一致させます。
  • OrderGetString(ORDER_COMMENT):注文のコメントを取得します。
  • 注文の銘柄、マジックナンバー、コメントがポジションのプロパティと一致していることを確認します。これにより、未決注文がポジションに関連していることが保証されます。

反対注文の特定と削除

switch(int(PositionGetInteger(POSITION_TYPE)))
{
   case POSITION_TYPE_BUY:
      if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_STOP)
      {
         Trade.OrderDelete(OrderGetTicket(O));
      }
      break;
   case POSITION_TYPE_SELL:
      if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_STOP)
      {
         Trade.OrderDelete(OrderGetTicket(O));
      }
      break;
   default:
      break;
}

  • PositionGetInteger(POSITION_TYPE):ポジションのタイプ(POSITION_TYPE_BUYまたはPOSITION_TYPE_SELL)を取得します。
  • OrderGetInteger(ORDER_TYPE):未決注文のタイプ(ORDER_TYPE_BUY_STOPまたはORDER_TYPE_SELL_STOP)を取得します。

Switch Case

  • ポジションが買いポジション(POSITION_TYPE_BUY)の場合、売り注文(ORDER_TYPE_SELL_STOP)を確認します。発見された場合、この売り止め注文はもはや必要ないため、削除されます。
  • 同様に、ポジションが売りポジション(POSITION_TYPE_SELL)の場合、保留中の買い逆指値注文(ORDER_TYPE_BUY_STOP)を確認します。見つかった場合、この買い逆指値注文は削除されます。

Trade.OrderDelete(OrderGetTicket(O)):このメソッドは、チケット番号を使用して未決注文を削除し、対応するポジションが建てられた後に、不要な反対注文を効果的に削除します。

SlippageReduction関数は、ポジションのストップロス(SL)レベルとテイクプロフィット(TP)レベルが期待値に正しく設定されるように設計されています。スリッページ(注文が執行された際の予想価格と実際の価格との差)によって、SLとTPが注文が執行された際の意図した値から乖離している場合、この関数能は、予想されたSLとTPを反映するようにポジションを調整します。

例:あるトレーダーがNFPを取引するために買い逆指値注文と売り逆指値注文を出したいと考えています。このトレーダーは、両方の注文で現在価格から100 pipsの価格差を希望しており、またリスクとリターンの比率を1:6にしたいため、100 pipsのストップロスと600 pipsのテイクプロフィットを設定しています。この場合、以下の画像を参照して、買いストップは1.13118に設定され、SLは1.13018、TPは1.13718に設定され、1:6のROIを維持しています。NFPイベントのボラティリティにより、買い取引は不利な価格である1.13134で実行され、操作ログメッセージ「order performed buy 0.01 at 1.13134 [#2 buy stop 0.01 EURUSD at 1.13118]」に示されています。

逆指値注文で発生したスリッページ

建値でのスリッページは、[実勢価格-予定価格]/ポイントとして計算されます。

予想価格:1.13118|実勢価格:1.13134|スリッページ:(1.13134 - 1.13118)/0.00001 -> 16 pips(価格差)

したがって、ROIとストップロスを維持するため、SLとTPの値は16 pipsずつ調整されます。

スリッページアクションの削減

//+------------------------------------------------------------------+
//|Function will attempt to re-adjust stop-losses or take-profit     |
//|values that have been changed due to slippage on an order when    |
//|opening.                                                          |
//+------------------------------------------------------------------+
void CTradeManagement::SlippageReduction(int SL,int TP,string COMMENT_COMMON)
  {
//--- Iterate through all open positions
   for(int i=0; i<PositionsTotal(); i++)
     {
      ulong ticket = PositionGetTicket(i);
      //--- Check if Position ticket is above zero
      if(ticket>0)
        {
         //--- Check if the Position's Symbol,Comment is correct
         if(PositionGetString(POSITION_SYMBOL)==CSymbol.GetSymbolName()
            &&StringFind(PositionGetString(POSITION_COMMENT),COMMENT_COMMON)>=0)
           {
            //--- Identify Position type
            switch(int(PositionGetInteger(POSITION_TYPE)))
              {
               case  POSITION_TYPE_BUY:
                  //--- set expect buy trade properties
                  SetBuyTrade(SL,TP,PositionGetDouble(POSITION_PRICE_OPEN));
                  //--- assign sl price
                  mySL = PositionGetDouble(POSITION_SL);
                  //--- assign tp price
                  myTP = PositionGetDouble(POSITION_TP);
                  //--- Normalize sl price
                  CSymbol.NormalizePrice(mySL);
                  mySL = double(DoubleToString(mySL,CSymbol.Digits()));
                  //--- Normalize tp price
                  CSymbol.NormalizePrice(myTP);
                  myTP = double(DoubleToString(myTP,CSymbol.Digits()));
                  //--- check if expected properties match actual trade properties
                  if((myBuyTrade.Get().Stop!=mySL||
                      myBuyTrade.Get().Take!=myTP)
                     &&Valid_Trade(POSITION_TYPE_BUY,myBuyTrade.Get().Open,
                                   myBuyTrade.Get().Stop,myBuyTrade.Get().Take))
                    {
                     //--- Modify position to respect expected properties
                     Trade.PositionModify(ticket,myBuyTrade.Get().Stop,myBuyTrade.Get().Take);
                    }
                  break;
               case POSITION_TYPE_SELL:
                  //--- set expect sell trade properties
                  SetSellTrade(SL,TP,PositionGetDouble(POSITION_PRICE_OPEN));
                  //--- assign sl price
                  mySL = PositionGetDouble(POSITION_SL);
                  //--- assign tp price
                  myTP = PositionGetDouble(POSITION_TP);
                  //--- Normalize sl price
                  CSymbol.NormalizePrice(mySL);
                  mySL = double(DoubleToString(mySL,CSymbol.Digits()));
                  //--- Normalize tp price
                  CSymbol.NormalizePrice(myTP);
                  myTP = double(DoubleToString(myTP,CSymbol.Digits()));
                  //--- check if expected properties match actual trade properties
                  if((mySellTrade.Get().Stop!=mySL||
                      mySellTrade.Get().Take!=myTP)
                     &&Valid_Trade(POSITION_TYPE_SELL,mySellTrade.Get().Open,
                                   mySellTrade.Get().Stop,mySellTrade.Get().Take))
                    {
                     //--- Modify position to respect expected properties
                     Trade.PositionModify(ticket,mySellTrade.Get().Stop,mySellTrade.Get().Take);
                    }
                  break;
               default:
                  break;
              }
           }
        }
     }
  }

関数の目的

この関数の目的は以下の通りです。

  1. 各ポジションを確認します。
  2. ポジションのストップロス値とテイクプロフィット値が予想値と一致しているか確認します。
  3. スリッページによって両者が一致しない場合、ポジションは正しいSLとTPの値を反映するように修正されます。

void CTradeManagement::SlippageReduction(int SL, int TP, string COMMENT_COMMON)

  • SlippageReduction:SLとTPが注文の執行中にスリッページの影響を受けた場合に調整します。

パラメータ

  • SL:ストップロス予想値
  • TP:テイクプロフィット予想値
  • COMMENT_COMMON:このEAに属する取引をフィルタリングして識別するための文字列

ポジションをループします。

for (int i = 0; i < PositionsTotal(); i++)
{
    ulong ticket = PositionGetTicket(i);

  • PositionsTotal():この関数は、ポジションの数を返します。
  • PositionGetTicket(i):インデックスiのポジションのチケット番号を取得します。チケットは、ポジションを一意に識別します。

ポジションチケットを検証します。

if (ticket > 0)

  • 次の手順に進む前に、そのポジションに有効なチケット番号があることを確認します。

ポジションプロパティを確認します。

if (PositionGetString(POSITION_SYMBOL) == CSymbol.GetSymbolName() &&
    StringFind(PositionGetString(POSITION_COMMENT), COMMENT_COMMON) >= 0)

  • PositionGetString(POSITION_SYMBOL):ポジションに関連する銘柄を取得します。
  • CSymbol.GetSymbolName():取引ストラテジーの銘柄名を取得します(例:EURUSD)。
  • PositionGetString(POSITION_COMMENT):ポジションに付けられたコメントを取得します。
  • StringFind():COMMENT_COMMONがポジションのコメントの一部であるかどうかを確認し、この関数が現在のストラテジーに関連するポジションのみを処理することを保証します。

ポジションタイプ(買いまたは売り)を特定し、処理します。

ケース1:買いポジション

case POSITION_TYPE_BUY:

  • この関数はBUYタイプのポジションを扱います。

予想される買い取引のプロパティを設定します。

SetBuyTrade(SL, TP, PositionGetDouble(POSITION_PRICE_OPEN));

  • SetBuyTrade(SL, TP, PositionGetDouble(POSITION_PRICE_OPEN)):買い取引の予想ストップロス、テイクプロフィット、建値を設定します。SetBuyTrade関数はこれらの期待値をmyBuyTradeオブジェクトに割り当てます。

実際のSL値とTP値を割り当てます。

mySL = PositionGetDouble(POSITION_SL);
myTP = PositionGetDouble(POSITION_TP);

  • PositionGetDouble(POSITION_SL):ポジションから実際のストップロス値を取得
  • PositionGetDouble(POSITION_TP):ポジションから実際のテイクプロフィット値を取得

SL値とTP値を正規化します。

CSymbol.NormalizePrice(mySL);
mySL = double(DoubleToString(mySL, CSymbol.Digits()));

CSymbol.NormalizePrice(myTP);
myTP = double(DoubleToString(myTP, CSymbol.Digits()));

  • NormalizePrice(mySL):ストップロス値を正しい小数点以下の桁数に正規化します(銘柄の特性に基づいて、例えば、通貨ペアは小数点以下が4桁または5桁の場合がある)。
  • DoubleToString(mySL, CSymbol.Digits()):正規化されたSL値を文字列に変換し、精度を確保するためにdoubleに戻します。
  • 同じプロセスをテイクプロフィット(myTP)値にも適用します。

予想と実際のSL/TPを比較します。

if ((myBuyTrade.Get().Stop != mySL || myBuyTrade.Get().Take != myTP) &&
    Valid_Trade(POSITION_TYPE_BUY, myBuyTrade.Get().Open, myBuyTrade.Get().Stop, myBuyTrade.Get().Take))
{
    Trade.PositionModify(ticket, myBuyTrade.Get().Stop, myBuyTrade.Get().Take);
}

  • MyBuyTrade.Get().Stop:予想ストップロス値を取得します。
  • mySL:ポジションから取得した実際のストップロス値
  • 実際のSLとTPの値が期待値と一致しない場合、この関数はTrade.PositionModifyを呼び出してポジションを更新し、正しいストップロスとテイクプロフィットを設定します。
  • Valid_Trade():ポジションを修正する前に、取引パラメータ(価格、SL、TP)が修正可能かどうかを確認します。

ポジションを変更します。

Trade.PositionModify(ticket, myBuyTrade.Get().Stop, myBuyTrade.Get().Take);

  • この関数は、チケットに関連するポジションを修正し、予想値に基づいて正しいストップロス値とテイクプロフィット値を反映させます。

ケース2:売りポジション

  • これは買いポジションのプロセスと同様です。

Valid_Trade関数は、取引のタイプ(買いまたは売り)、および現在の銘柄のプロパティに関連するストップロスとテイクプロフィットのレベルを管理するルールに基づいて、取引のパラメータ(価格、ストップロス、テイクプロフィットのレベルなど)が有効かどうかを確認します。この関数は、取引が有効であればtrueを、有効でなければfalseを返します。

//+------------------------------------------------------------------+
//|Check if a trade is valid                                         |
//+------------------------------------------------------------------+
bool CTradeManagement::Valid_Trade(ENUM_POSITION_TYPE Type,double Price,double SL,double TP)
  {
//--- Identify Position type
   switch(Type)
     {
      case  POSITION_TYPE_BUY:
         if((Price<TP||TP==0)&&(Price>SL||SL==0)&&
            ((int((Price-SL)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(SL==0))&&
            ((int((TP-Price)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(TP==0))&&
            Price>0
           )
           {
            //--- Trade properties are valid.
            return true;
           }
         break;
      case POSITION_TYPE_SELL:
         if((Price>TP||TP==0)&&(Price<SL||SL==0)&&
            ((int((Price-TP)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(TP==0))&&
            ((int((SL-Price)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(SL==0))&&
            Price>0
           )
           {
            //--- Trade properties are valid.
            return true;
           }
         break;
      default://Unknown
         return false;
         break;
     }
//--- Trade properties are not valid.
   Print("Something went wrong, SL/TP/Open-Price is incorrect!");
   return false;
  }

関数の目的

この関数の目的は以下の通りです。

  1. 買いまたは売りポジションの取引パラメータ(Price、SL、TP)を検証します。
  2. ストップロスとテイクプロフィットのレベルが建値に対して正しく設定され、銘柄のストップレベルの要件を満たしていることを確認します。

パラメータ

bool CTradeManagement::Valid_Trade(ENUM_POSITION_TYPE Type, double Price, double SL, double TP)

  • Type:ポジションタイプ(POSITION_TYPE_BUY:買いポジション、またはPOSITION_TYPE_SELL:売りポジション)
  • Price:取引開始価格
  • SL:取引のストップロスレベル
  • TP:取引のテイクプロフィットレベル

Switch文

この関数は、switch文を使用して、異なるタイプのポジション(買いまたは売り)を処理し、それぞれのケースに対して異なる検証ロジックを適用します。

ケース1:買いポジション

case POSITION_TYPE_BUY:

買いポジションのロジックは以下の条件を確認します。

テイクプロフィット(TP)を検証します。

if ((Price < TP || TP == 0)

  • テイクプロフィットレベルは建値より大きい(Price < TP)か、ゼロ(テイクプロフィットなし)に設定することができます。

ストップロス(SL)を検証します。

(Price > SL || SL == 0)

  • ストップロスレベルは建値より小さい(Price > SL)か、ゼロ(ストップロスなし)に設定することができます。

銘柄のストップレベルを検証します。

  • 銘柄のストップレベルは、価格とストップロス/テイクプロフィットの間の最小距離を定義します。
  • ストップロスとテイクプロフィットのレベルは、建値から最低距離(ストップレベルと同じかそれ以上)でなければなりません。

ストップロス距離を検証します。

((int((Price - SL) / CSymbol.Point()) >= CSymbol.StopLevel()) || SL == 0)

  • 価格とストップロスの差は、銘柄のストップレベル(ポイント単位)以上でなければなりません。ストップロスがゼロに設定されている場合、この条件は無視されます。

テイクプロフィット距離を検証します。

((int((TP - Price) / CSymbol.Point()) >= CSymbol.StopLevel()) || TP == 0)

  • テイクプロフィットと価格の差は、ストップレベルの要件も満たさなければなりません。

最終的な検証をおこないます。

&& Price > 0

  • 価格はゼロより大きくなければなりません。

これらの条件がすべて満たされた場合、その取引は有効とみなされ、関数はtrueを返します。

ケース2:売りポジション

売りポジションの場合、ロジックは似ているが逆です。

以下が、デフォルトのケースです。

default:
    return false;

ポジションタイプがPOSITION_TYPE_BUYでもPOSITION_TYPE_SELLでもない場合、この関数はfalseを返し、取引が有効でないことを示します。

取引プロパティが無効な場合

検証のいずれかが失敗した場合、この関数はエラーメッセージを記録し、falseを返します。

Print("Something went wrong, SL/TP/Open-Price is incorrect!");
return false;

このメッセージは、ストップロス、テイクプロフィット、または建値が正しくないことを示します。

このコードでは、ストップロス(SL)、テイクプロフィット(TP)、価格、注文偏差の条件を検証することによって、逆指値注文(買い逆指値注文または売り逆指値注文)が有効かどうかを確認するValid_Orderメソッドを定義しています。この関数は、注文が有効であればtrueを返し、そうでなければfalseを返します。

//+------------------------------------------------------------------+
//|Check if stop order is valid                                      |
//+------------------------------------------------------------------+
bool CTradeManagement::Valid_Order(ENUM_ORDER_TYPE Type,double Price,double SL,double TP)
  {
//--- Identify Order type
   switch(Type)
     {
      case  ORDER_TYPE_BUY_STOP:
         if((Price<TP||TP==0)&&(Price>SL||SL==0)&&(Price>CSymbol.Ask())&&
            ((int((Price-SL)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(SL==0))&&
            ((int((TP-Price)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(TP==0))&&
            myDeviation>=uint(CSymbol.StopLevel())
           )
           {
            //--- Order properties are valid.
            return true;
           }
         break;
      case ORDER_TYPE_SELL_STOP:
         if((Price>TP||TP==0)&&(Price<SL||SL==0)&&(Price<CSymbol.Bid())&&
            ((int((Price-TP)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(TP==0))&&
            ((int((SL-Price)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(SL==0))&&
            myDeviation>=uint(CSymbol.StopLevel())
           )
           {
            //--- Order properties are valid.
            return true;
           }
         break;
      default://Other
         return false;
         break;
     }
//--- Order properties are not valid.
   Print("Something went wrong, SL/TP/Deviation/Open-Price is incorrect!");
   return false;
  }

概要

この関数は、逆指値注文タイプに基づいて検証をおこないます。

  1. 買い逆指値注文:買い逆指値注文は、現在の市場価格(Ask価格)より上に発注され、市場価格がこのレベル以上に達したときに発動されます。
  2. 売り逆指値注文:売り逆指値注文は、現在の市場価格(bid価格)より下に発注され、市場価格がこのレベル以下に下落したときに発動されます。

パラメータ

bool CTradeManagement::Valid_Order(ENUM_ORDER_TYPE Type, double Price, double SL, double TP)

  • Type:逆指値注文タイプ(ORDER_TYPE_BUY_STOPまたはORDER_TYPE_SELL_STOPのいずれか)
  • Price:逆指値注文が設定される価格
  • SL:逆指値注文に関連するストップロス価格
  • TP:逆指値注文に関連するテイクプロフィット価格

Switch文

switch文は異なる注文タイプを扱い、それぞれに異なる検証ルールを適用します。

ケース1:買い逆指値注文

case ORDER_TYPE_BUY_STOP:

買い逆指値注文の場合、以下の条件を満たす必要があります。

テイクプロフィット(TP)を検証します。

(Price < TP || TP == 0)

テイクプロフィット価格は注文価格より大きくなければならない(Price < TP)、またはゼロに設定することもできる(テイクプロフィットなし)。

ストップロス(SL)を検証します。

(Price > SL || SL == 0)

逆指値は注文価格より低くなければなりません(Price > SL)、またはゼロに設定することもできます(逆指値なし)。

Askプライスを上回る価格:

(Price > CSymbol.Ask())

注文価格は現在の売値より大きくなければなりません。これは買い逆指値注文の重要な条件です。

銘柄のストップレベルの検証:ストップロス距離を検証します。

((int((Price - SL) / CSymbol.Point()) >= CSymbol.StopLevel()) || SL == 0)

注文価格とストップロスの差は、銘柄のストップレベル(注文価格とストップロスレベルの間の許容可能な最小距離(ポイント単位))以上である必要があります。ストップロスがゼロに設定されている場合、この条件は回避されます。

テイクプロフィット距離を検証します。

((int((TP - Price) / CSymbol.Point()) >= CSymbol.StopLevel()) || TP == 0)

テイクプロフィットと注文価格の差は、ストップレベルの要件も満たさなければなりません。

価格乖離を検証します。

myDeviation >= uint(CSymbol.StopLevel())

価格乖離(スリッページ許容範囲)は銘柄のストップレベル以上でなければなりません。乖離により、市場価格が設定したストップレベルを多少超えて動いても、注文は執行されます。

これらの条件がすべて満たされた場合、買い逆指値注文は有効とみなされ、この関数はtrueを返します。

ケース2:売り逆指値注文

売り逆指値注文の場合、検証ロジックは買い逆指値注文と逆になります。

以下が、デフォルトのケースです。

default:
    return false;

注文タイプがORDER_TYPE_BUY_STOPでもORDER_TYPE_SELL_STOPでもない場合、この関数はfalseを返し、注文が有効でないことを示します。

エラー処理:

条件が失敗し、関数が注文を検証できない場合、エラーメッセージが記録されます。

Print("Something went wrong, SL/TP/Deviation/Open-Price is incorrect!");

このメッセージは、ストップロス、テイクプロフィット、価格偏差、または注文価格に何か問題があり、注文が無効であることをユーザーに知らせます。

以下のコードでは、買い注文のプロパティを設定する関数SetBuyStopを定義しています。与えられた入力に基づき、ストップロス(SL)とテイクプロフィット(TP)の価格を計算し、注文に割り当てます。 この関数は以下の手順を実行します。

  1. 買い逆指値注文の建値を計算します。
  2. 建値に対するストップロスとテイクプロフィットの価格を計算し、設定します。
  3. 価格が銘柄の精度(小数点以下の桁数)に合うように正規化します。

//+------------------------------------------------------------------+
//|Will set buy-stop order properties                                |
//+------------------------------------------------------------------+
void CTradeManagement::SetBuyStop(int SL,int TP)
  {
//-- Get Open-price
   myOpenPrice=CSymbol.Ask()+myDeviation*CSymbol.Point();
   CSymbol.NormalizePrice(myOpenPrice);
   NormalizeDouble(myOpenPrice,CSymbol.Digits());
//--- Get SL value
   mySL=SL*CSymbol.Point();
   mySL=myOpenPrice-mySL;
//--- Normalize the SL Price
   CSymbol.NormalizePrice(mySL);
   NormalizeDouble(mySL,CSymbol.Digits());
//--- Get TP value
   myTP=TP*CSymbol.Point();
   myTP+=myOpenPrice;
//--- Normalize the TP Price
   CSymbol.NormalizePrice(myTP);
   NormalizeDouble(myTP,CSymbol.Digits());
//--- Set BuyStop properties
   myBuyStop.Set(myOpenPrice,myTP,mySL);
  }

パラメータ

  • SL:ストップロス値(ポイント単位)
  • TP:テイクプロフィット値(ポイント単位)

void CTradeManagement::SetBuyStop(int SL, int TP)

SLとTPはどちらも、建値からストップロスまたはテイクプロフィットのレベルまでのポイント数(価格の増分)を表す整数値です。

手順1:買い逆指値注文の建値を計算する

myOpenPrice = CSymbol.Ask() + myDeviation * CSymbol.Point();

  • CSymbol.Ask()は、銘柄(通貨ペアまたは資産)の現在のAsk価格を取得します。
  • myDeviation * CSymbol.Point()は、建値が現在のAsk価格より上に配置されるように、ポイント単位で偏差(価格バッファ)を追加します。CSymbol.Point()は、銘柄の1ポイントの大きさ(最小の価格変化)を返します。

正規化

CSymbol.NormalizePrice(myOpenPrice);
NormalizeDouble(myOpenPrice, CSymbol.Digits());

  • CSymbol.NormalizePrice(myOpenPrice)は、価格を銘柄の精度(つまり、有効な価格フォーマット)に合わせます。
  • NormalizeDouble(myOpenPrice, CSymbol.Digits())は、銘柄の精度(Csymbol.Digits())で定義された正しい小数点以下の桁数に価格が丸められることを保証します。

手順2:ストップロス(SL)価格を計算・正規化する

mySL = SL * CSymbol.Point();
mySL = myOpenPrice - mySL;

  • SL * CSymbol.Point()は、整数のストップロス値(ポイント単位)を価格差に変換します。
  • myOpenPrice - mySLは、建値からストップロス値(ポイント単位)を引いてストップロス価格を計算します。これは、買い逆指値注文の逆指値が建値より下に置かれるからです。

正規化

CSymbol.NormalizePrice(mySL);
NormalizeDouble(mySL, CSymbol.Digits());

  • ストップロス価格は、上で説明したように正規化され、正しい精度で四捨五入されます。

手順3:テイクプロフィット(TP)価格の計算と正規化

myTP = TP * CSymbol.Point();
myTP += myOpenPrice;

  • TP * CSymbol.Point()は、整数のテイクプロフィット値(ポイント単位)を価格差に変換します。
  • myTP+=myOpenPriceは、買い逆指値注文のテイクプロフィットが建値より上に置かれるため、この値を建値に追加します。

正規化

CSymbol.NormalizePrice(myTP);
NormalizeDouble(myTP, CSymbol.Digits());

  • テイクプロフィット価格は、ストップロス価格や建値と同様に正規化され、丸められます。

手順4:逆指値注文への計算値の割り当て

myBuyStop.Set(myOpenPrice, myTP, mySL);

  • myBuyStop.Set(myOpenPrice, myTP, mySL)は、計算された建値、テイクプロフィット、ストップロスの値をmyBuyStop注文オブジェクトに割り当てます。
  • myBuyStopオブジェクトは、マーケットに買い逆指値注文を出すために必要なプロパティを保持するようになります。

SetBuyTrade関数は、買いポジションのプロパティを設定します。それは、取引のための正しい建値、ストップロス(SL)、およびテイクプロフィット(TP)を計算し、割り当てます。 この関数は、買いポジション(買い逆指値注文のような未決注文ではない)のパラメータを設定するために使用します。買いポジションは現在の市場価格で建てられ、建値に対してストップロスとテイクプロフィットのレベルが指定されます。

パラメータ

  • SL:ストップロス値(ポイント単位)(価格刻み)
  • TP:テイクプロフィット値(ポイント単位)
  • OP:取引の建値(ポジションが建てられた価格)

手順1:建値の設定

myOpenPrice = OP;
CSymbol.NormalizePrice(myOpenPrice);
myOpenPrice = double(DoubleToString(myOpenPrice, CSymbol.Digits()));

  • myOpenPrice = OP:この関数は、指定された建値(OP)を受け取り、それをmyOpenPriceに代入します。
  • CSymbol.NormalizePrice(myOpenPrice):銘柄の精度に従って建値を正規化し、小数点以下の桁数が正しく一致するようにします。
  • myOpenPrice = double(DoubleToString(myOpenPrice, CSymbol.Digits())):価格はさらに文字列に変換され、銘柄に適した小数点以下の桁数(精度)でdoubleに戻されます。これにより、価格値の取り扱いの一貫性が確保され、浮動小数点数の精度に関する問題が回避されます。

手順2:ストップロス(SL)価格を計算・正規化する

mySL = SL * CSymbol.Point();
mySL = myOpenPrice - mySL;

  • SL * CSymbol.Point():ストップロス値をポイントから実際の価格差に変換します。CSymbol.Point()は、1ポイントのサイズ(銘柄の可能な最小の価格増分)を返します。
  • myOpenPrice - mySL:逆指値は、建値から計算された逆指値の値を引いて設定されます。買いポジションでは、ストップロスは建値より下に置かれ、過度の損失から取引を保護します。

正規化

CSymbol.NormalizePrice(mySL);
mySL = double(DoubleToString(mySL, CSymbol.Digits()));

  • ストップロス価格は、正しい精度を持つように正規化され、適切な小数点以下の桁数に丸められます。

手順3:テイクプロフィット(TP)価格の計算と正規化

myTP = TP * CSymbol.Point();
myTP += myOpenPrice;

  • TP * CSymbol.Point():テイクプロフィット値をポイントから価格差に変換します。
  • myTP += myOpenPrice:買いポジションの場合、テイクプロフィットは建値より上に置かれるため、テイクプロフィット価格は建値に加算されます。

正規化

CSymbol.NormalizePrice(myTP);
myTP = double(DoubleToString(myTP, CSymbol.Digits()));

  • テイクプロフィット価格は正規化され、銘柄の精度に合うように四捨五入されます。

手順4:計算値を買いポジションに割り当てる

myBuyTrade.Set(myOpenPrice, myTP, mySL);

  • myBuyTrade.Set(myOpenPrice, myTP, mySL):計算された建値、ストップロス、テイクプロフィットの値はmyBuyTradeオブジェクトに割り当てられます。このオブジェクトは、建てられるまたは変更される買いポジションのすべての関連プロパティを保持します。

OpenBuyStop関数は、指定された損切り(SL)、テイクプロフィット(TP)、マジックナンバー、およびオプションのコメントで買い逆指値注文を出そうとします。

パラメータ

  • SL:ストップロス値(ポイント単位)
  • TP:テイクプロフィット値(ポイント単位)
  • Magic:注文を識別するためのユニークなマジックナンバー(通常EAに使用される)
  • COMMENT:(オプション)識別のために注文に添付する文字列コメント

//+------------------------------------------------------------------+
//|Will attempt to open a buy-stop order                             |
//+------------------------------------------------------------------+
bool CTradeManagement::OpenBuyStop(int SL,int TP,ulong Magic,string COMMENT=NULL)
  {
   SetBuyStop(SL,TP);
//--- Set the order type for Risk management calculation
   SetOrderType(ORDER_TYPE_BUY);
//--- Set open price for Risk management calculation
   OpenPrice = myBuyStop.Get().Open;
//--- Set close price for Risk management calculation
   ClosePrice = myBuyStop.Get().Stop;
//--- Set Trade magic number
   Trade.SetExpertMagicNumber(Magic);
//--- Check if there are any open trades or opened deals or canceled deals already
   if(!OpenOrder(ORDER_TYPE_BUY_STOP,Magic,COMMENT)&&!OpenedDeal(DEAL_TYPE_BUY,Magic,COMMENT)
//--- Check if the buy-stop properties are valid
      &&Valid_Order(ORDER_TYPE_BUY_STOP,myBuyStop.Get().Open,myBuyStop.Get().Stop,myBuyStop.Get().Take))
     {
      //--- Iterate through the Lot-sizes if they're more than max-lot
      for(double i=Volume();i>=CSymbol.LotsMin()&&
          /* Check if current number of orders +1 more orders is less than
                 account orders limit.*/
          (PositionsTotal()+Account.numOrders()+1)<Account.LimitOrders()
          ;i-=CSymbol.LotsMax())
        {
         //--- normalize Lot-size
         NormalizeLotsize(i);
         /* Open buy-stop order with a Lot-size not more than max-lot and set order expiration
         to the Symbol's session end time for the current day.
         */
         if(!Trade.BuyStop((i>CSymbol.LotsMax())?CSymbol.LotsMax():i,myBuyStop.Get().Open,
                           CSymbol.GetSymbolName(),myBuyStop.Get().Stop,myBuyStop.Get().Take,
                           ORDER_TIME_SPECIFIED,CTS.SessionEnd(),COMMENT))
           {
            //--- Order failed to open
            return false;
           }
        }
     }
   else
     {
      //--- Order failed
      return false;
     }
//--- Return trade result.
   return true;
  }

手順1:ストッププロパティの設定

SetBuyStop(SL, TP);

  • この行はSetBuyStopメソッド(先に説明)を呼び出し、買い逆指値注文の建値、ストップロス、テイクプロフィットを計算して設定します。
  • 結果はmyBuyStopオブジェクトに格納され、買い逆指値注文のキープロパティ(建値、SL、TP)を保持します。

手順2:リスク管理のための注文タイプの設定

SetOrderType(ORDER_TYPE_BUY);

  • 内部注文タイプをORDER_TYPE_BUYに設定します。これは買い逆指値注文ですが、基本的には買い注文であることに変わりはなく、ストップロス、テイクプロフィット、ポジションサイジングなどのリスク管理指標の計算に使用されます。

手順3:リスク管理のための建値と終値の設定

OpenPrice = myBuyStop.Get().Open;
ClosePrice = myBuyStop.Get().Stop;

  • OpenPrice:買い逆指値注文の計算された建値(myBuyStop.Get().Open)に設定されます。
  • ClosePrice:買い逆指値注文の計算された逆指値価格(myBuyStop.Get().Stop)に設定されます。

手順4:マジックナンバーを設定する

Trade.SetExpertMagicNumber(Magic);

  • Trade.SetExpertMagicNumberを使用して注文のマジックナンバーを設定します。マジックナンバーは注文を一意に識別するもので、EAが開いた取引を管理する際に役立ちます。

手順5:未決済の取引や約定を確認する

if (!OpenOrder(ORDER_TYPE_BUY_STOP, Magic, COMMENT) && !OpenedDeal(DEAL_TYPE_BUY, Magic, COMMENT)

  • OpenOrder:この関数は、同じマジックナンバーとコメントで既に出されている買い注文が存在するかどうかを確認します。存在した場合、新しい注文を出しません。
  • OpenDeal:この関数は、同じマジックナンバーとコメントを使った買いポジションがすでに建てられているかどうかを確認します。すでに建てられている場合、新しいポジションを建てません。

手順6:買い注文のプロパティを検証する

&& Valid_Order(ORDER_TYPE_BUY_STOP, myBuyStop.Get().Open, myBuyStop.Get().Stop, myBuyStop.Get().Take))

  • Valid_Orderメソッド(先に説明)を使用して、計算された買い注文のプロパティが有効かどうかを確認します。このメソッドは、建値、ストップロス、テイクプロフィットが正しいこと、注文が銘柄のルール(例えば、最小ストップレベル、ポイント値など)を守っていることを確認します。

手順7:ロットサイズを反復する

for (double i = Volume(); i >= CSymbol.LotsMin() && (PositionsTotal() + Account.numOrders() + 1) < Account.LimitOrders(); i -= CSymbol.LotsMax())

  • Volume():注文の現在の取引数量(ロットサイズ)を取得します。
  • ループは現在のボリューム(i = Volume())から開始され、次のボリュームまで続きます。

    1. ロットサイズは最小ロットサイズ(CSymbol.LotsMin())以上です。
    2. ポジション数と口座の既存注文の合計は、口座の注文限度額(Account.LimitOrders())よりも少ないです。

これは、注文が出来高制限に準拠するように、各反復ごとに最大ロットサイズ(CSymbol.LotsMax())を増分してロットサイズを縮小します。

手順8:ロットサイズの正規化

NormalizeLotsize(i);

  • ロットサイズ(i)は正規化され、銘柄の精度規則を満たすように調整されます。

手順9:買い逆指値注文の試み

if (!Trade.BuyStop((i > CSymbol.LotsMax()) ? CSymbol.LotsMax() : i, myBuyStop.Get().Open, CSymbol.GetSymbolName(), 
myBuyStop.Get().Stop, myBuyStop.Get().Take, ORDER_TIME_SPECIFIED, CTS.SessionEnd(), COMMENT))

  • Trade.BuyStop():以下のパラメータを使用して買い逆指値注文の発注を試みます。

    1. Lot size:ロットサイズの上限はCSymbol.LotsMax()(銘柄の最大許容ロットサイズ)です。(i)が最大ロットサイズより大きければ、最大ロットサイズを使用し、そうでなければ、現在のロットサイズ(i)を使用します。
    2. Open price:買い逆指値注文が執行されるべき価格
    3. Symbol name:取引銘柄(通貨ペアなど)の名前
    4. Stop-loss:計算された注文のストップロス価格
    5. Take-profit:計算された注文のテイクプロフィット価格
    6. Order expiration time:注文は、その日の銘柄の取引セッションの終了時に失効するように設定されます(CTS.SessionEnd())
    7. Comment:先に提供されたオプションのコメント

注文が出せなかった場合、この関数はfalseを返し、買い逆指値注文が正常に発注されなかったことを示します。

手順10:結果を戻す

return true;

  • 注文が正常に出された場合、この関数はtrueを返し、買い逆指値注文が正常に出されたことを示します。

OpenSellStop関数

OpenSellStop関数のロジックは、先に説明したOpenBuyStop関数と似ています。

OpenStops関数は、買い逆指値注文と売り逆指値注文の両方を開こうとします。 

//+------------------------------------------------------------------+
//|This function will open both buy-stop and sell-stop orders for    |
//|StopOrdersType(STOP ORDERS)                                       |
//+------------------------------------------------------------------+
bool CTradeManagement::OpenStops(int SL,int TP,ulong Magic,string COMMENT=NULL)
  {
//--- Set buy-stop properties
   SetBuyStop(SL,TP);
//--- Set sell-stop properties
   SetSellStop(SL,TP);
//--- Set the order type for Risk management calculation
   SetOrderType(ORDER_TYPE_BUY);
//--- Set open price for Risk management calculation
   OpenPrice = myBuyStop.Get().Open;
//--- Set close price for Risk management calculation
   ClosePrice = myBuyStop.Get().Stop;
//--- Set Trade magic number
   Trade.SetExpertMagicNumber(Magic);
//--- Check if there are any open trades or opened deals or canceled deals already
   if(!OpenOrder(ORDER_TYPE_BUY_STOP,Magic,COMMENT)&&!OpenedDeal(DEAL_TYPE_BUY,Magic,COMMENT)
      &&!OpenedDeal(DEAL_TYPE_BUY_CANCELED,Magic,COMMENT)
//--- Check if the buy-stop properties are valid
      &&Valid_Order(ORDER_TYPE_BUY_STOP,myBuyStop.Get().Open,myBuyStop.Get().Stop,myBuyStop.Get().Take)
      &&!OpenOrder(ORDER_TYPE_SELL_STOP,Magic,COMMENT)
      &&!OpenedDeal(DEAL_TYPE_SELL,Magic,COMMENT)&&!OpenedDeal(DEAL_TYPE_SELL_CANCELED,Magic,COMMENT)
//--- Check if the sell-stop properties are valid
      &&Valid_Order(ORDER_TYPE_SELL_STOP,mySellStop.Get().Open,mySellStop.Get().Stop,mySellStop.Get().Take))
     {
      //--- Iterate through the Lot-sizes if they're more than max-lot
      for(double i=Volume();i>=CSymbol.LotsMin()&&
          /* Check if current number of orders +2 more orders is less than
             account orders limit.*/
          (PositionsTotal()+Account.numOrders()+2)<Account.LimitOrders()
          ;i-=CSymbol.LotsMax())
        {
         //--- normalize Lot-size
         NormalizeLotsize(i);
         /* Open orders with a Lot-size not more than max-lot and set order expiration
         to the Symbol's session end time for the current day.
         */
         if(!Trade.BuyStop((i>CSymbol.LotsMax())?CSymbol.LotsMax():i,myBuyStop.Get().Open,
                           CSymbol.GetSymbolName(),myBuyStop.Get().Stop,myBuyStop.Get().Take,
                           ORDER_TIME_SPECIFIED,CTS.SessionEnd(),COMMENT)
            ||!Trade.SellStop((i>CSymbol.LotsMax())?CSymbol.LotsMax():i,mySellStop.Get().Open,
                              CSymbol.GetSymbolName(),mySellStop.Get().Stop,mySellStop.Get().Take,
                              ORDER_TIME_SPECIFIED,CTS.SessionEnd(),COMMENT))
           {
            //--- one or more orders failed to open.
            return false;
           }
        }
     }
   else
     {
      //--- Orders failed
      return false;
     }
//--- Return trade result.
   return true;
  }

手順1:買い逆指値注文と売り逆指値注文のプロパティを設定する

SetBuyStop(SL, TP);
SetSellStop(SL, TP);

  • SetBuyStop:このメソッドでは、買い逆指値注文の建値、ストップロス、テイクプロフィットを計算し、設定します。これらの値はmyBuyStopオブジェクトに格納されます。
  • SetSellStop:同様に、このメソッドは、mySellStopオブジェクトに格納されている、売り注文の建値、ストップロス、およびテイクプロフィットを計算し、設定します。

手順2:リスク管理のために注文タイプを設定する

SetOrderType(ORDER_TYPE_BUY);

  • 買い逆指値注文と売り逆指値注文の両方が発注されているにもかかわらず、リスク管理計算のための内部注文タイプを買い注文に設定します。この設定は、後で買い注文のストップロスとテイクプロフィットに基づいてリスクを評価するために使用されます。

手順3:リスク管理のための建値と終値の設定

OpenPrice = myBuyStop.Get().Open;
ClosePrice = myBuyStop.Get().Stop;

  • OpenPrice:買い逆指値注文の計算された建値
  • ClosePrice:買い逆指値注文の計算された逆指値価格。これらの価格は、内部のリスク管理計算に使用されます。

手順4:マジックナンバーを設定する

Trade.SetExpertMagicNumber(Magic);

  • EAが管理する注文を一意に識別するマジックナンバーが取引に割り当てられます。

手順5:既存の注文や取引を確認する

if(!OpenOrder(ORDER_TYPE_BUY_STOP, Magic, COMMENT) && !OpenedDeal(DEAL_TYPE_BUY, Magic, COMMENT)
   && !OpenedDeal(DEAL_TYPE_BUY_CANCELED, Magic, COMMENT)

  • OpenOrder:同じマジックナンバーとコメントを持つ買い逆指値注文が既に存在するかどうかを確認します。存在した場合、新しい買い逆指値注文は出されません。
  • OpenDeal:同じマジックナンバーとコメントを持つ、有効かキャンセルされている買いポジションがすでに存在するかどうかを確認します。存在した場合、新しい買い逆指値注文は出されません。

手順6:買い注文のプロパティを検証する

&& Valid_Order(ORDER_TYPE_BUY_STOP, myBuyStop.Get().Open, myBuyStop.Get().Stop, myBuyStop.Get().Take)

  • Valid_Order:買い逆指値注文のプロパティ(建値、ストップロス、テイクプロフィット)を検証し、それらが銘柄のルール(例えば、最小ストップレベル)に適合していることを確認します。検証に合格したら、売り注文の確認に進みます。

手順7:既存の売り注文または取引を確認する

&& !OpenOrder(ORDER_TYPE_SELL_STOP, Magic, COMMENT) && !OpenedDeal(DEAL_TYPE_SELL, Magic, COMMENT)
&& !OpenedDeal(DEAL_TYPE_SELL_CANCELED, Magic, COMMENT)

  • 買い逆指値のチェックと同様に、これらの条件は、同じマジックナンバーとコメントを持つ売り逆指値注文、有効な売り取引、またはキャンセルされた売り取引がすでにあるかどうかを確認します。存在した場合、この関数は新たな売り逆指値注文の発注を回避します。

手順8:売り注文のプロパティを検証する

&& Valid_Order(ORDER_TYPE_SELL_STOP, mySellStop.Get().Open, mySellStop.Get().Stop, mySellStop.Get().Take))

  • Valid_Order:逆指値売り注文のプロパティ(建値、逆指値、テイクプロフィット)を検証します。検証に合格した場合、買い逆指値注文と売り逆指値注文の両方を出します。

手順9:ロットサイズを反復する

for (double i = Volume(); i >= CSymbol.LotsMin() && (PositionsTotal() + Account.numOrders() + 2) < Account.LimitOrders(); i -= CSymbol.LotsMax())

  • Volume():現在の取引数量(ロットサイズ)を取得します。
  • ループは、全数量(i = Volume())から開始し、許容される最大ロットサイズ(CSymbol.LotsMax())を超える場合は、ロットサイズ(i -= CSymbol.LotsMax())を減らします。
  • 未決ポジションと未決注文の合計数が、口座のリミット(Account.LimitOrders())の範囲内であることを保証します。

このループは、ロットサイズが銘柄の最大許容ロットサイズを超える場合、注文を複数の小さな注文に分割することを保証します。

手順10:ロットサイズの正規化

NormalizeLotsize(i);

  • ロットサイズ(i)は、銘柄の許容精度に適合するように正規化されます。

手順11:買い逆指値注文と売り逆指値注文の試み

if(!Trade.BuyStop((i > CSymbol.LotsMax()) ? CSymbol.LotsMax() : i, myBuyStop.Get().Open,
                  CSymbol.GetSymbolName(), myBuyStop.Get().Stop, myBuyStop.Get().Take,
                  ORDER_TIME_SPECIFIED, CTS.SessionEnd(), COMMENT)
   || !Trade.SellStop((i > CSymbol.LotsMax()) ? CSymbol.LotsMax() : i, mySellStop.Get().Open,
                     CSymbol.GetSymbolName(), mySellStop.Get().Stop, mySellStop.Get().Take,
                     ORDER_TIME_SPECIFIED, CTS.SessionEnd(), COMMENT))

  • Trade.BuyStop:以下のパラメータを使用して買い逆指値注文を試みます。
    • Lot size:現在のロットサイズ(i)が許容される最大値(Csymbol.LotsMax())を超えている場合、許容される最大ロットサイズで注文を発注します。
    • Open price:買い逆指値注文の計算された建値
    • Symbol name:取引商品の名前
    • Stop-loss:買い逆指値注文のストップロス価格
    • Take-profit:買い逆指値注文のテイクプロフィット価格
    • Expiration time:その日の銘柄の取引セッションの終了時刻(CTS.SessionEnd())
    • Comment:注文を説明するオプションのコメント
  • Trade.SellStop:同様に、買い逆指値注文と同じロジックで売り逆指値注文を出そうとします。

どちらかの注文が出せなかった場合、この関数はfalseを返します。

手順12結果を戻す

return true;

  • 買い逆指値注文と売り逆指値注文の両方が正常に発注された場合、この関数はtrueを返します。処理の一部が失敗した場合、この関数はfalseを返します。

CloseTrades関数は、指定されたコメント(COMMENT_COMMON)に一致するすべての建玉を決済するように設計されています。 

//+------------------------------------------------------------------+
//|Function will attempt to close all trades depending on the        |
//|position comment                                                  |
//+------------------------------------------------------------------+
void CTradeManagement::CloseTrades(string COMMENT_COMMON)
  {
//--- Iterate through all open positions
   for(int i=0; i<PositionsTotal(); i++)
     {
      ulong ticket = PositionGetTicket(i);
      //--- Check if Position ticket is above zero
      if(ticket>0)
        {
         //--- Check if the Position's Symbol,Comment is correct
         if(PositionGetString(POSITION_SYMBOL)==CSymbol.GetSymbolName()
            &&StringFind(PositionGetString(POSITION_COMMENT),COMMENT_COMMON)>=0)
           {
            //--- close trade.
            Trade.PositionClose(ticket);
           }
        }
     }
  }

手順1:すべてのポジションを反復する

for (int i = 0; i < PositionsTotal(); i++)

  • PositionsTotal():ポジションの総数を返します。
  • このループは、現在建てられているすべてのポジションを繰り返し処理します。変数iはポジションのインデックスで、0からPositionsTotal() - 1の範囲です。

手順2:ポジションのチケット番号を取得する

ulong ticket = PositionGetTicket(i);

  • PositionGetTicket(i):インデックスiのポジションのチケット番号を取得します。チケット番号は、各ポジションに固有の識別子です。
  • チケット番号は変数ticketに格納されます。

手順3:チケットが有効かどうかを確認する

if (ticket > 0)

  • これは、チケット番号が0より大きいかどうかを確認し、検索されたチケットが有効であることを確認してから処理を進めます。チケット番号0は、指定されたインデックスにポジションが存在しないことを示し、これは有効な状態ではありません。

手順4:ポジション銘柄とコメントの検証

if (PositionGetString(POSITION_SYMBOL) == CSymbol.GetSymbolName() 
    && StringFind(PositionGetString(POSITION_COMMENT), COMMENT_COMMON) >= 0)

  • PositionGetString(POSITION_SYMBOL):インデックスiのポジション(通貨ペアなど)の銘柄名を取得します。
  • CSymbol.GetSymbolName():CSymbolオブジェクトに関連付けられた銘柄名を取得します。
  • 最初の条件は、ポジションの銘柄がCSymbolで管理されている銘柄と一致するかどうかを確認します。
  • PositionGetString(POSITION_COMMENT):ポジションに付随するコメント文字列を取得します。
  • StringFind(PositionGetString(POSITION_COMMENT), COMMENT_COMMON):指定されたCOMMENT_COMMON文字列がポジションのコメントに存在するかどうかを確認します。
    • コメントにCOMMENT_COMMONが含まれている場合、この関数は一致が始まるインデックスを返します。
    • 条件 >= 0は、コメントに部分文字列が含まれている場合のみ合格することを保証します。

これにより、銘柄とコメントが一致するポジションのみがクローズ対象として選択されます。

手順5:取引を終了する

Trade.PositionClose(ticket);

  • Trade.PositionClose(ticket):チケット番号で特定されるポジションをクローズしようとします。
  • ポジションが条件(正しい銘柄であり、指定されたコメントを含む)に一致する場合、このメソッドを使用してクローズされます。


結論

この記事では、逆指値注文を出し、オープンする前に取引と注文の有効性を確認するコードを実装しました。FundamentalModewと呼ばれる関数を作成しました。この関数は、反対売買の逆指値注文を管理することで、特別な取引モードを処理します。さらに、逆指値注文のスリッページ削減が実施され、ニュースリリースによる不安定な市場環境下での価格スリッページのリスクが軽減されました。

重要なポイント

  • 実行の精度:取引管理クラスは、取引の発注、変更、決済のすべての側面を処理し、不安定な市場でも取引の執行が正確におこなわれるようにします。
  • リアルタイム調整:ストップロスとテイクプロフィットを動的に調整する機能により、EAはリアルタイムの市場変化に確実に対応し、より良いリスク管理を可能にします。
  • スリッページ管理:スリッページを考慮し、取引パラメータを動的に調整するロジックを実装することで、Trade Managementクラスは、取引が可能な限り意図した条件に近い状態で実行されるようにし、潜在的な損失を削減します。

お時間を割いていただきありがとうございました。次回の記事では、さらに多くの価値を提供できることを楽しみにしています:) 

MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/16169

添付されたファイル |
NewsTrading_Part5.zip (598.37 KB)
MacOSでのMetaTrader 5 MacOSでのMetaTrader 5
macOS上のMetaTrader 5取引プラットフォーム用の特別なインストーラーを提供します。これは、アプリケーションをネイティブにインストールできる本格的なウィザードです。インストーラーは、システムの識別、最新のWineバージョンのダウンロードとインストール、設定の適用、その後のMetaTraderのインストールまで、すべての手順を自動で実行します。インストールが完了すると、すぐにプラットフォームを使用できます。
Connexusのリクエスト(第6回):HTTPリクエストとレスポンスの作成 Connexusのリクエスト(第6回):HTTPリクエストとレスポンスの作成
Connexusライブラリ連載第6回目では、HTTPリクエストの構成要素全体に焦点を当て、リクエストを構成する各コンポーネントを取り上げます。そして、リクエスト全体を表現するクラスを作成し、これまでに作成したクラスを統合します。
主成分を用いた特徴量選択と次元削減 主成分を用いた特徴量選択と次元削減
この記事では、Luca Puggini氏とSean McLoone氏による論文「Forward Selection Component Analysis: Algorithms and Applications」に基づき、修正版のForward Selection Component Analysis (FSCA)アルゴリズムの実装について詳しく解説します。
Controlsクラスを使用してインタラクティブなMQL5ダッシュボード/パネルを作成する方法(第2回):ボタンの応答性の追加 Controlsクラスを使用してインタラクティブなMQL5ダッシュボード/パネルを作成する方法(第2回):ボタンの応答性の追加
この記事では、ボタンの応答性を有効にすることで、静的なMQL5ダッシュボードパネルをインタラクティブなツールへと変換することに焦点を当てます。GUIコンポーネントの機能を自動化し、ユーザーのクリックに適切に反応する方法を探究します。この記事の最後には、ユーザーのエンゲージメントと取引体験を向上させる動的なインターフェイスを構築します。