English Русский 中文 Español Deutsch Português
preview
リスク管理(第5回):リスク管理システムをエキスパートアドバイザーに統合する

リスク管理(第5回):リスク管理システムをエキスパートアドバイザーに統合する

MetaTrader 5 |
25 0
Niquel Mendoza
Niquel Mendoza


はじめに

連載「リスク管理」の最終回へようこそ。リスク管理を導入することで取引結果がどのように変化するのか、そもそも効果があるのか、動的リスク管理のメリットとデメリットは何か、そしてどのような状況で最も有効なのかについて解説します。

これらの疑問に答えるため、これまでの記事で紹介したOrder Blocksインジケーターを利用したシンプルなエキスパートアドバイザー(EA)を作成します。

まず、このインジケーターを最適化し、バックテストを高速化・効率化するともに、EAの最適化を容易にします。

次に、EAの作成手順および各パラメータの定義について解説します。また、OnTradeTransactionなどの主要イベントを適切に実装し、それらを正しい関数内で処理する方法についても説明します。

本記事で提起された疑問に答えるため、異なるパラメータの組み合わせを用いてEAのテストを4回実行します。これにより、損失および利益の制限を設定した場合と設定しない場合のトレード結果を比較し、動的リスク管理が最も効果的に機能する状況について評価します。


Order Blocksインジケーターの改善

まず、Order Blocksインジケーターの最適化から始めます。これまでのパフォーマンスは十分とは言えない状態でした。分析の結果、ローソク足データの処理を最適化し、不要なループを削除することで、パフォーマンスを改善できることが分かりました。

1. オーダーブロック検出ループの最適化

インジケーターの最初のループでは、毎回すべてのローソク足を走査する必要はないため、反復回数を削減します。インジケーターが読み込まれた際、OnCalculateのprev_calculatedパラメータは0になります。 このパラメータは、すでに計算済みのローソク足の数を示します。初回実行時はまだ計算がおこなわれていないため、prev_calculatedは0です。そのため初回計算ではRango_universal_busquedaからローソク足6までループし、prev_calculatedが0より大きい場合はローソク足6のみを確認します。

int inicio = prev_calculated == 0 ? Rango_universal_busqueda : 6;

    for(int i = inicio  ; i  > 5  ; i--)
     {
      //----

     }

2. オーダーブロックのミティゲーション判定

オーダーブロックがミティゲーションされたかどうか(価格がブロックレベルに到達または超過したかどうか)を判定するために、2つの関数を改善します。1つは買いブロック用、もう1つは売りブロック用です。どちらの関数もローソク足の高値と安値のみを使用することで簡素化しています。

上昇オーダーブロックのミティゲーション判定

datetime  mitigados_alcsitas(double price, const double &lowArray[], const  datetime &Time[], datetime start, datetime end)
 {
  int startIndex = iBarShift(_Symbol, PERIOD_CURRENT, start);
  int endIndex = iBarShift(_Symbol, PERIOD_CURRENT, end);

  NormalizeDouble(price, _Digits);
  for(int i = startIndex - 2 ; i >= endIndex + 1 ; i--)
   {
    if(price > lowArray[i])
     {
      return Time[i]; //si encuentra que si hubo retorna el tiempo de la vela donde hubo la mitigacion
      Print("el orderblock tuvo mitigaciones", TimeToString(end));
     }
   }

  return 0; //En caso no se haya encontrado  niguna mitigacion retorna 0
 }

下降オーダーブロックのミティゲーション判定

//+------------------------------------------------------------------+
datetime  mitigado_bajista(double price, const  double &highArray[], const datetime &Time[], datetime start, datetime end)
 {
  int startIndex = iBarShift(_Symbol, PERIOD_CURRENT, start);
  int endIndex = iBarShift(_Symbol, PERIOD_CURRENT, end);
  NormalizeDouble(price, _Digits);
  for(int i = startIndex - 2 ; i >= endIndex + 1 ; i--)
   {
    if(highArray[i] > price)
     {
      return Time[i]; //retorna el time de la vela encontrada
     }
   }
  return 0; // no se mitigo hasta el momento
 }

両方の関数では、ローソク足の高値と安値を直接使用することで、パラメータ数を削減し、ループの実行時間をわずかに短縮します。

さらに、これらの変更をesOb_mitigado_array_...関数にも適用しています。従来はiOpenやiCloseなどの関数を呼び出していましたが、これは必ずしも必要ではありません。たとえば、ローソク足の終値がオーダーブロックレベルを下回っている場合、そのローソク足の安値も同様にそのレベルを下回っているためです。そのため、ミティゲーション判定はブロックの種類に応じて、高値または安値のみを使用する形に簡略化できます。

上昇オーダーブロック

//+------------------------------------------------------------------+
datetime esOb_mitigado_array_alcista(OrderBlocks &newblock, datetime end)
 {
  int endIndex = iBarShift(_Symbol, PERIOD_CURRENT, end);
  NormalizeDouble(newblock.price2, _Digits);
  for(int i = 0 ; i <  endIndex - 2  ; i++)
   {
    double low = iLow(_Symbol, PERIOD_CURRENT, i);
    if(newblock.price2 >= low)
     {
      newblock.mitigated = true;
      newblock.time2 = iTime(_Symbol, _Period, i);
      return newblock.time2; //retorna el time de la vela encontrada
     }
   }
  return 0; // no se mitigo hasta el momento
 }

下降オーダーブロック

//+------------------------------------------------------------------+
datetime esOb_mitigado_array_bajista(OrderBlocks &newblock, datetime end)
 {
  int endIndex = iBarShift(_Symbol, PERIOD_CURRENT, end);
  NormalizeDouble(newblock.price2, _Digits);
  for(int i = 0 ; i <  endIndex - 2  ; i++)
   {
    double high = iHigh(_Symbol, PERIOD_CURRENT, i);
    if(high >= newblock.price2)
     {
      newblock.mitigated = true;
      newblock.time2 = iTime(_Symbol, _Period, i);
      return newblock.time2;
     }
   }
  return 0; // no se mitigo hasta el momento
 }

3. 配列に要素を追加するためのテンプレート関数

配列へ要素を追加するため、より効率的なテンプレート関数をいくつか追加します。従来は、特定の型のみを受け付ける専用関数を個別に作成していました。たとえば、Order_Blocks構造体を追加する場合、その型専用の関数を別途実装する必要がありました。

テンプレート関数を使用することで、異なる型に対して同じ処理を共通化できます。このケースでは、配列へ要素を追加するための汎用関数を作成しています。

template <typename S>
bool AddArray(S &Array[], const S &Value)
 {
  for(int i = 0 ; i < ArraySize(Array) ; i++)
   {
    if(Array[i].name == Value.name)
      return false;
   }
  ArrayResize(Array, Array.Size() + 1);
  Array[Array.Size() - 1] = Value;
  return true;
 }

template <typename X>
void AddArrayNoVerification(X &array[], const X &value)
 {
  ArrayResize(array, array.Size() + 1);
  array[array.Size() - 1] = value;
 }

4. 要素を名前によって削除するためのテンプレート関数

次に、名前を指定して要素を削除するテンプレート関数を作成します。この関数を使用するためには、対象の配列がnameというpublicメンバーを持つ構造体またはクラスである必要があります。この関数は、targetNameパラメータを基準に要素を削除します。これにより、ミティゲーション済みオーダーブロックの配列から不要な要素を効率的に削除できます。

template<typename T>
bool DeleteArrayBiName(T &array[], const string targetName)
 {
  int size = ArraySize(array);
  int index = -1;

// Buscar el índice y desplazar elementos en un solo bucle
  for(int i = 0; i < size; i++)
   {
    if(array[i].name == targetName)
     {
      index = i;
     }
    if(index != -1 && i < size - 1)
     {
      array[i] = array[i + 1]; // Desplaza los elementos
     }
   }

  if(index == -1)
    return false;

  if(size > 1)
    ArrayResize(array, size - 1);
  else
    ArrayFree(array); // Si el array tenía solo un elemento, se libera completamente

  return true;
 }

5. オーダーブロック削除の新機能

以前のバージョンでは、オーダーブロックがミティゲーションされると、その名前を補助配列に保存していましたが、オーダーブロック自体はメイン配列内に残ったままでした。今回の改善では、ミティゲーション済みオーダーブロックの名前のみを補助配列に保存し、オーダーブロック本体をメイン配列から削除するように変更しました  

下降オーダーブロック

    static bool buscar_obb = true;
    static datetime time_b = 0;
    string curr_elimiandor_obb[];

    for(int i = 0; i < ArraySize(ob_bajistas); i++)
     {
      datetime mitigadoTime = esOb_mitigado_array_bajista(ob_bajistas[i], ob_bajistas[i].time1);

      if(ob_bajistas[i].mitigated == false)
       {
        if(ObjectFind(ChartID(), ob_bajistas[i].name) < 0)
         {
          RectangleCreate(ChartID(), ob_bajistas[i].name, 0, ob_bajistas[i].time1, ob_bajistas[i].price1,
                          time[0], ob_bajistas[i].price2, Color_Order_Block_Bajista, Witdth_order_block, Fill_order_block, Back_order_block, STYLE_SOLID);
          sellOrderBlockBuffer[iBarShift(_Symbol, _Period, ob_bajistas[i].time1)] = ob_bajistas[i].price2;
         }
        else
          ObjectMove(ChartID(), ob_bajistas[i].name, 1, time[0], ob_bajistas[i].price2);
       }
      else
       {
        Alert("El order block bajista esta siendo mitigado: ", TimeToString(ob_bajistas[i].time1));
        AddArrayNoVerification(pricetwo_eliminados_obb, ob_bajistas[i].name);
        AddArrayNoVerification(curr_elimiandor_obb, ob_bajistas[i].name); 

        if(buscar_obb == true)
         {
          time_b = iTime(_Symbol, _Period, 0);
          buscar_obb = false;
         }
       }
     }

    for(int i = 0; i < ArraySize(curr_elimiandor_obb) ; i++)
     {
      DeleteArrayBiName(ob_bajistas, curr_elimiandor_obb[i]);
     }

上昇オーダーブロック

    static bool buscar_oba = true;
    static datetime time_a = 0;
    string curr_elimiandor_oba[];

    for(int i = 0; i < ArraySize(ob_alcistas); i++)
     {
      datetime mitigadoTime = esOb_mitigado_array_alcista(ob_alcistas[i], ob_alcistas[i].time1);

      if(ob_alcistas[i].mitigated == false)
       {
        if(ObjectFind(ChartID(), ob_alcistas[i].name) < 0)
         {
          RectangleCreate(ChartID(), ob_alcistas[i].name, 0, ob_alcistas[i].time1, ob_alcistas[i].price1,
                          time[0], ob_alcistas[i].price2, Color_Order_Block_Alcista, Witdth_order_block, Fill_order_block, Back_order_block, STYLE_SOLID);
          buyOrderBlockBuffer[iBarShift(_Symbol, _Period, ob_alcistas[i].time1)] = ob_alcistas[i].price2;
         }
        else
          ObjectMove(ChartID(), ob_alcistas[i].name, 1, time[0], ob_alcistas[i].price2); //por el contrario si si existe el objeto lo unico que haremos es actulizarlo al tiempo actual usando el punto de anclaje 1
       }
      else
       {
        Alert("El order block alcista esta siendo mitigado: ", TimeToString(ob_alcistas[i].time1));
        AddArrayNoVerification(pricetwo_eliminados_oba, ob_alcistas[i].name);
        AddArrayNoVerification(curr_elimiandor_oba, ob_alcistas[i].name);

        if(buscar_oba == true)
         {
          buscar_oba = false;
          time_a = iTime(_Symbol, _Period, 1);
         }
       }
     }

    for(int i = 0; i < ArraySize(curr_elimiandor_oba) ; i++)
     {
      DeleteArrayBiName(ob_alcistas, curr_elimiandor_oba[i]);
     }

6. オブジェクトを削除する関数

チャート上のオブジェクトを削除する際にも、いくつかの変更が必要になります。以前は、ミティゲーション済みオーダーブロックを再処理しないようにするため、その名前を配列へ保存していました。現在は、これらのオーダーブロックをメイン配列から削除する新しい関数を実装しています。そのため、後からチャート上のオブジェクトを削除できるように、ミティゲーション済みオーダーブロックの名前を補助配列へ保存する必要があります。

以下は、補助配列も走査してオブジェクトを削除するように修正したDelete_Objects関数です。

void Eliminar_Objetos()
 {
  ObjectsDeleteAll(0, "ENTRY", -1, -1);
  for(int i = 0; i < ArraySize(pricetwo_eliminados_oba) ; i++) ObjectDelete(0,pricetwo_eliminados_oba[i]);
  for(int i = 0; i < ArraySize(pricetwo_eliminados_obb) ; i++) ObjectDelete(0,pricetwo_eliminados_obb[i]); 
  for(int i = 0; i < ArraySize(ob_alcistas) ; i++) ObjectDelete(0,ob_alcistas[i].name);
  for(int i = 0; i < ArraySize(ob_bajistas) ; i++) ObjectDelete(0,ob_bajistas[i].name);
  
  ArrayFree(pricetwo_eliminados_oba);
  ArrayFree(pricetwo_eliminados_obb);
 }

7. 事前定義配列の使用

ロジックは、MQLがOnCalculate内で提供される事前定義配列を使用することで最適化しています。これらの配列を利用して、オーダーブロックを計算します。使用する主要なローソク足データは、open、close、high、low、tick_volumeです。

この方法により、オーダーブロックを扱う際のデータ管理およびデータの解釈が容易になります。

    ArraySetAsSeries(open, true);
    ArraySetAsSeries(close, true);
    ArraySetAsSeries(high, true);
    ArraySetAsSeries(low, true);
    ArraySetAsSeries(time, true);
    ArraySetAsSeries(tick_volume, true);
    ArraySetAsSeries(atr, true);


エキスパートアドバイザーの作成とそのパラメータの定義

まずはEAを作成しましょう。

1. テンプレートからEAを作成します。

パート1

2. 名前と著者名を指定します。

パート2

3.OnTradeTransactionイベントのみを選択します。

パート3

4. 完了します。

パート4

次に、EAを正しく動作させるために必要なパラメータを作成します。まず、マジックナンバーやすでにOrder Blocksインジケーターで使用しているデフォルト設定など、EAの基本パラメータから定義していきます。

1. 一般パラメータ

一般パラメータを設定する前に、インジケーターで使用しているテイクプロフィットおよびストップロスの種類を定義する列挙型を作成します。内容はインジケーター側と同じです。

enum ENUM_TP_SL_STYLE
 {
  ATR = 0,
  POINT = 1
 };

以下は、追加した各パラメータの概要です。

  • Magic: EAによる取引を識別するためのマジックナンバーです。他のEAや手動トレードと区別するために使用します。

  • timeframe_order_block: オーダーブロックを検出する時間足を設定します。

  • Rango_universal_busqueda: EAが候補となるオーダーブロックを検索する際に、何本前のローソク足まで遡るかを指定します。

  • Witdth_order_block: オーダーブロックを表す矩形の線の太さを設定します。

  • Back_order_blockFill_order_block: オーダーブロックを表す矩形の背景および塗りつぶしに関する描画パラメータです。

  • Color_Order_Block_BassistColor_Order_Block_Bullish: それぞれ下降オーダーブロックおよび上昇オーダーブロックの色を設定します。

  • tp_sl_style: テイクプロフィットおよびストップロスの計算方法を指定します(ATRまたはPOINT)。

  • Atr_Multiplier_1Atr_Multiplier_2: ATR方式を使用する際の乗数です。

  • TP_POINTSL_POINT: POINT方式を使用する場合のテイクプロフィットおよびストップロス値です。

sinput group "--- Order Block EA settings ---"
input ulong Magic = 545244; //Magic number
input ENUM_TIMEFRAMES timeframe_order_block = PERIOD_M5; //Order block timeframe

sinput group "-- Order Block --"
input int  Rango_universal_busqueda = 500; //search range of order blocks
input int  Witdth_order_block = 1; //Width order block

input bool Back_order_block = true; //Back order block?
input bool Fill_order_block = true; //Fill order block?

input color Color_Order_Block_Bajista = clrRed; //Bearish order block color
input color Color_Order_Block_Alcista = clrGreen; //Bullish order block color

sinput group "-- Strategy --"
input ENUM_TP_SL_STYLE tp_sl_style = POINT;//tp and sl style

sinput group "- ATR "
input double Atr_Multiplier_1 = 1.5;//Atr multiplier 1
input double Atr_Multiplier_2 = 2.0;//Atr multiplier 2

sinput group "- POINT "
input int TP_POINT = 1000; //TL in Points
input int SL_POINT = 1000; //SL in Points

2. リスク管理パラメータ

続いて、リスク管理で使用するEAのパラメータを定義します。まずは一般的なリスク管理パラメータです。一般パラメータは以下の通りです。

  • Lote_Type: 動的ロット(1取引ごとのリスク管理に基づくロット)を使用するか、固定ロットを使用するかを指定します。

  • lot: Lote_TypeがFixedに設定されている場合に使用するロットサイズです。

  • risk_mode: 個人口座か、FTMOなどのプロップファーム口座かを選択します。

  • get_mode: ロットサイズの計算方法を指定します。ストップロス値を基準に、1取引あたりのリスクに応じてロットサイズを調整します。

  • prop_firm_balance: FTMO口座、または同様のルールを持つプロップファーム口座を使用する場合の初期口座残高を指定します。前回までの記事で説明したように、このパラメータは1取引あたりの最大損失および1日の最大損失を計算するために使用します。

input ENUM_LOTE_TYPE Lote_Type = Dinamico; //Lote Type
input double lote = 0.1; //lot size (only for fixed lot)
input ENUM_MODE_RISK_MANAGEMENT risk_mode = personal_account;//type of risk management mode
input ENUM_GET_LOT get_mode = GET_LOT_BY_STOPLOSS_AND_RISK_PER_OPERATION; //How to get the lot
input double prop_firm_balance = 1000; //If risk mode is Prop Firm FTMO, then put your ftmo account balance

3. 最大損失に関するパラメータ(日次、週次、および合計)

次に、最大損失(ML)、最大週次損失(MWL)、最大日次損失(MDL)を管理するためのパラメータを定義します。これらの制限は、それぞれ損失制限の計算方法および適用方法を決定する3つの変数に基づいています。

  • percentage_or_money_..._input: 選択した計算モードに応じて、割合または金額を指定します。値が0に設定されている場合、その損失制限は使用されません。

  • mode_calculation_...: パラメータを割合として評価するか、金額として評価するかを指定します。

  • applied_percentages_...: 割合を適用する基準を指定します(残高、口座利益など)。

sinput group "- ML/Maxium loss/Maxima perdida -"
input double percentage_or_money_ml_input = 0; //percentage or money (0 => not used ML)
input ENUM_RISK_CALCULATION_MODE mode_calculation_ml = percentage; //Mode calculation Max Loss
input ENUM_APPLIED_PERCENTAGES applied_percentages_ml = Balance; //ML percentage applies to:

sinput group "- MWL/Maximum weekly loss/Perdida maxima semanal -"
input double percentage_or_money_mwl_input  = 0; //percentage or money (0 => not used MWL)
input ENUM_RISK_CALCULATION_MODE mode_calculation_mwl = percentage; //Mode calculation Max weekly Loss
input ENUM_APPLIED_PERCENTAGES applied_percentages_mwl = Balance;//MWL percentage applies to:

sinput group "- MDL/Maximum  daily loss/Perdida maxima diaria -"
input double percentage_or_money_mdl_input  = 0; //percentage or money (0 => not used MDL)
input ENUM_RISK_CALCULATION_MODE mode_calculation_mdl = percentage; //Mode calculation Max daily loss
input ENUM_APPLIED_PERCENTAGES applied_percentages_mdl = Balance;//MDL percentage applies to:

4. GMLPO:1取引あたりの最大リスク

続いて、1取引あたりの最大損失を設定します。このセクションはより複雑で、3つを超えるパラメータを含みます。前述の通り、ここでは動的リスク管理も導入します。

4.1 GMLPOの一般パラメータ

まず、動的リスク管理で使用する5つの主要パラメータを定義します。これには、最大日次損失・最大週次損失・最大累計損失ですでに使用した3つのパラメータに加え、2つの追加パラメータが含まれます。追加された2つのパラメータでは、1取引あたりのリスクを調整する必要があるかどうかをシステムが確認する頻度、および使用する動的リスク設定タイプを指定できます。追加で使用する列挙型は以下の通りです。

//--- Enumeration of the types of dynamic operational risk
enum ENUM_OF_DYNAMIC_MODES_OF_GMLPO
 {
  DYNAMIC_GMLPO_FULL_CUSTOM, //Customisable dynamic risk per operation
  DYNAMIC_GMLPO_FIXED_PARAMETERS,//Risk per operation with fixed parameters
  NO_DYNAMIC_GMLPO //No dynamic risk for risk per operation
 };
//--- Enumeration to determine when to review a decrease in the initial balance to modify the risk per operation
enum ENUM_REVISION_TYPE
 {
  REVISION_ON_CLOSE_POSITION, //Check GMLPO only when closing positions
  REVISION_ON_TICK //Check GMLPO on all ticks
 };
sinput group "- GMLPO/Gross maximum loss per operation/Porcentaje a arriesgar por operacion -"
input ENUM_OF_DYNAMIC_MODES_OF_GMLPO mode_gmlpo = DYNAMIC_GMLPO_FULL_CUSTOM; //Select GMLPO mode:
input ENUM_REVISION_TYPE revision_type_gmlpo = REVISION_ON_CLOSE_POSITION; //Type revision
input double percentage_or_money_gmlpo_input  = 1.0; //percentage or money (0 => not used GMLPO)
input ENUM_RISK_CALCULATION_MODE mode_calculation_gmlpo = percentage; //Mode calculation Max Loss per operation
input ENUM_APPLIED_PERCENTAGES applied_percentages_gmlpo = Balance;//GMPLO percentage applies to:
  • mode_gmlpo: 1取引あたりのリスクを動的にするかどうかを決定します。動的リスク設定モードのいずれかを選択します。動的リスクを使用しない場合はNO_DYNAMIC_GMLPOを選択します。
  • revision_type_gmlpo: 1取引あたりのリスクを調整するために口座の有効証拠金をチェックするタイミングを指定します。ポジション決済時、またはティックごとに評価をおこなうかを選択します。
  • percentage_or_money_gmlpo_input: 1取引あたりのリスクを決定するための基準となる割合または金額を指定します。値が0の場合、GMLPO機能は無効になります。
  • mode_calculation_gmlpo: percentage_or_money_gmlpo_inputを割合として扱うか、固定金額として扱うかを決定します。
  • applied_percentages_gmlpo: 割合計算モードを使用する場合に、その割合を適用する基準(例:BalanceまたはEquity)を設定します。

注記:ここで使用される各列挙型は、前回までの記事で説明したものと同様であり、内部でのリスク計算方法の詳細を定義しています。 

4.2 動的GMLPO設定

前述の通り、動的リスクの設定には2つのモードがあります。完全にカスタマイズ可能なモードと、固定パラメータモードです。前回の記事では、この設計を採用した理由について説明しました。要点として、文字列をパラメータとして使用すると最適化ができないため、最適な動的リスクパラメータを見つけることが難しくなります。そのため、この問題を解決するために列挙型を導入しています。

4.2.1 カスタマイズ可能な動的GMLPO

このモードでは、残高が何パーセント減少した時点でリスクを変更するか、またその際の新しい1取引あたりのリスク値をユーザーが指定します。このモードは最大限の柔軟性を提供しますが、文字列ベースのため、ストラテジーテスターでの最適化には対応していません。

sinput group "-- Optional GMLPO settings, Dynamic GMLPO"
sinput group "--- Full customizable dynamic GMLPO"
input string note1 = "subtracted from your total balance to establish a threshold.";  //This parameter determines a specific percentage that will be
input string str_percentages_to_be_reviewed = "2,5,7,9"; //percentages separated by commas.
input string note2 = "a new risk level will be triggered on your future trades: "; //When the current balance (equity) falls below this threshold
input string str_percentages_to_apply = "1,0.7,0.5,0.33"; //percentages separated by commas.
input string note3 = "0 in both parameters => do not use dynamic risk in gmlpo"; //Note:

  • str_percentages_to_be_reviewed: 1取引あたりのリスクを変更するトリガーとなる閾値を定義するための、昇順にソートされた割合のリストを保持します。デフォルト値として"2,5,7,9"が設定されています。これは、口座利益(パーセンテージ換算)が2%を下回った場合にリスクが調整され、str_percentages_to_applyの最初の値(1)が適用されることを意味します。

  • str_percentages_to_apply:1取引あたりのリスクが変更された際に適用される、新しいリスク割合のリストを保持します。

4.2.2 固定パラメータによる動的GMLPO

固定パラメータを使用して動的リスクを設定したい場合は、EAの設定内に専用セクションが用意されています。このモードの動作はカスタマイズモードと類似していますが、最大4つのパラメータを直接設定できます。4つすべてを使用する必要がない場合、たとえば3つだけ使用する場合は、未使用のフィールドには必ず0を入力します。たとえば、セクション「-- 4 --」で3つのモディファイアのみを使用する場合、残りの2つのパラメータは0に設定する必要があります。

sinput group "--- Fixed dynamic GMLPO with parameters"
sinput group "- 1 -"
input string note1_1 = "subtracted from your total balance to establish a threshold.";  //This parameter determines a specific percentage that will be
input double inp_balance_percentage_to_activate_the_risk_1 = 2.0; //percentage 1 that will be exceeded to modify the risk separated by commas
input string note2_1 = "a new risk level will be triggered on your future trades: "; //When the current balance (equity) falls below this threshold
input double inp_percentage_to_be_modified_1 = 1.0;//new percentage 1 to which the gmlpo is modified
sinput group "- 2 -"
input double inp_balance_percentage_to_activate_the_risk_2 = 5.0;//percentage 2 that will be exceeded to modify the risk separated by commas
input double inp_percentage_to_be_modified_2 = 0.7;//new percentage 2 to which the gmlpo is modified
sinput group "- 3 -"
input double inp_balance_percentage_to_activate_the_risk_3 = 7.0;//percentage 3 that will be exceeded to modify the risk separated by commas
input double inp_percentage_to_be_modified_3 = 0.5;//new percentage 3 to which the gmlpo is modified
sinput group "- 4 -"
input double inp_balance_percentage_to_activate_the_risk_4 = 9.0;//percentage 4 that will be exceeded to modify the risk separated by commas
input double inp_percentage_to_be_modified_4 = 0.33;//new percentage 4  1 to which the gmlpo is modified

5. 最大日次利益(MDP)

リスク管理パラメータの一覧を完成させるために、最大日次利益(MDP)パラメータを追加します。このパラメータは、最大損失/利益制限で共通して使用される3つのパラメータに加え、新たにmdp_is_strictパラメータを含みます。このパラメータは、「1取引ごとの最大利益チェックモード」が有効かどうかを示します。

mdp_is_strictがfalseの場合、その日の損失状況に関係なく、MDPの目標値を超えれば条件達成とみなされます。たとえば、利益目標が10ドルで、その日に4ドルの損失が発生した後に5ドルの利益を得た場合でも、合計として目標を上回っていれば最大日次利益を達成したと判定されます。一方で、mdp_is_strictの値がtrueの場合、単に目標利益に到達するだけでは不十分です。まずその日の損失をすべて回復し、その上で追加の利益を上げて初めてMDP達成とみなされます。たとえば、目標が10ドルでその日に5ドルの損失がある場合、まずその5ドルを回復し、その後さらに10ドルの利益を上げる必要があります。

sinput group "- MDP/Maximum daily profit/Maxima ganancia diaria -"
input bool mdp_is_strict = true; //MDP is strict?
input double percentage_or_money_mdp_input = 0; //percentage or money (0 => not used MDP)
input ENUM_RISK_CALCULATION_MODE mode_calculation_mdp = percentage; //Mode calculation Max Daily Profit
input ENUM_APPLIED_PERCENTAGES applied_percentages_mdp = Balance;//MDP percentage applies to:

6. 取引セッション

低ボラティリティ期間や不適切な時間帯での取引を避けるため、取引セッションを定義するオプションを追加しています。

現在時刻が指定された時間範囲内にある場合のみ、EAは取引を実行し、それ以外の時間帯では取引をおこないません。

sinput group "--- Session ---"
input         char hora_inicio = 16;//start hour to operate (0-23)
input         char min_inicio = 30;//start minute to operate (0-59)
input         char hora_fin = 18;//end hour to operate (1-23)
input         char min_fin =0;//end minute to operate (0-59)

以下がパラメータの全リストです。

sinput group "--- Order Block EA settings ---"
input ulong Magic = 545244; //Magic number
input ENUM_TIMEFRAMES timeframe_order_block = PERIOD_M5; //Order block timeframe

sinput group "-- Order Block --"
input int  Rango_universal_busqueda = 500; //search range of order blocks
input int  Witdth_order_block = 1; //Width order block

input bool Back_order_block = true; //Back order block?
input bool Fill_order_block = true; //Fill order block?

input color Color_Order_Block_Bajista = clrRed; //Bearish order block color
input color Color_Order_Block_Alcista = clrGreen; //Bullish order block color

sinput group "-- Strategy --"
input ENUM_TP_SL_STYLE tp_sl_style = POINT;//tp and sl style

sinput group "- ATR "
input double Atr_Multiplier_1 = 1.5;//Atr multiplier 1
input double Atr_Multiplier_2 = 2.0;//Atr multiplier 2

sinput group "- POINT "
input int TP_POINT = 1000; //TL in Points
input int SL_POINT = 1000; //SL in Points

sinput group "--- Risk Management ---"
input ENUM_LOTE_TYPE Lote_Type = Dinamico; //Lote Type
input double lote = 0.1; //lot size (only for fixed lot)
input ENUM_MODE_RISK_MANAGEMENT risk_mode = personal_account;//type of risk management mode
input ENUM_GET_LOT get_mode = GET_LOT_BY_STOPLOSS_AND_RISK_PER_OPERATION; //How to get the lot
input double prop_firm_balance = 1000; //If risk mode is Prop Firm FTMO, then put your ftmo account balance

sinput group "- ML/Maxium loss/Maxima perdida -"
input double percentage_or_money_ml_input = 0; //percentage or money (0 => not used ML)
input ENUM_RISK_CALCULATION_MODE mode_calculation_ml = percentage; //Mode calculation Max Loss
input ENUM_APPLIED_PERCENTAGES applied_percentages_ml = Balance; //ML percentage applies to:

sinput group "- MWL/Maximum weekly loss/Perdida maxima semanal -"
input double percentage_or_money_mwl_input  = 0; //percentage or money (0 => not used MWL)
input ENUM_RISK_CALCULATION_MODE mode_calculation_mwl = percentage; //Mode calculation Max weekly Loss
input ENUM_APPLIED_PERCENTAGES applied_percentages_mwl = Balance;//MWL percentage applies to:

sinput group "- MDL/Maximum  daily loss/Perdida maxima diaria -"
input double percentage_or_money_mdl_input  = 0; //percentage or money (0 => not used MDL)
input ENUM_RISK_CALCULATION_MODE mode_calculation_mdl = percentage; //Mode calculation Max daily loss
input ENUM_APPLIED_PERCENTAGES applied_percentages_mdl = Balance;//MDL percentage applies to:

sinput group "- GMLPO/Gross maximum loss per operation/Porcentaje a arriesgar por operacion -"
input ENUM_OF_DYNAMIC_MODES_OF_GMLPO mode_gmlpo = DYNAMIC_GMLPO_FULL_CUSTOM; //Select GMLPO mode:
input ENUM_REVISION_TYPE revision_type_gmlpo = REVISION_ON_CLOSE_POSITION; //Type revision
input double percentage_or_money_gmlpo_input  = 1.0; //percentage or money (0 => not used GMLPO)
input ENUM_RISK_CALCULATION_MODE mode_calculation_gmlpo = percentage; //Mode calculation Max Loss per operation
input ENUM_APPLIED_PERCENTAGES applied_percentages_gmlpo = Balance;//GMPLO percentage applies to:

sinput group "-- Optional GMLPO settings, Dynamic GMLPO"
sinput group "--- Full customizable dynamic GMLPO"
input string note1 = "subtracted from your total balance to establish a threshold.";  //This parameter determines a specific percentage that will be
input string str_percentages_to_be_reviewed = "2,5,7,9"; //percentages separated by commas.
input string note2 = "a new risk level will be triggered on your future trades: "; //When the current balance (equity) falls below this threshold
input string str_percentages_to_apply = "1,0.7,0.5,0.33"; //percentages separated by commas.
input string note3 = "0 in both parameters => do not use dynamic risk in gmlpo"; //Note:

sinput group "--- Fixed dynamic GMLPO with parameters"
sinput group "- 1 -"
input string note1_1 = "subtracted from your total balance to establish a threshold.";  //This parameter determines a specific percentage that will be
input double inp_balance_percentage_to_activate_the_risk_1 = 2.0; //percentage 1 that will be exceeded to modify the risk separated by commas
input string note2_1 = "a new risk level will be triggered on your future trades: "; //When the current balance (equity) falls below this threshold
input double inp_percentage_to_be_modified_1 = 1.0;//new percentage 1 to which the gmlpo is modified
sinput group "- 2 -"
input double inp_balance_percentage_to_activate_the_risk_2 = 5.0;//percentage 2 that will be exceeded to modify the risk separated by commas
input double inp_percentage_to_be_modified_2 = 0.7;//new percentage 2 to which the gmlpo is modified
sinput group "- 3 -"
input double inp_balance_percentage_to_activate_the_risk_3 = 7.0;//percentage 3 that will be exceeded to modify the risk separated by commas
input double inp_percentage_to_be_modified_3 = 0.5;//new percentage 3 to which the gmlpo is modified
sinput group "- 4 -"
input double inp_balance_percentage_to_activate_the_risk_4 = 9.0;//percentage 4 that will be exceeded to modify the risk separated by commas
input double inp_percentage_to_be_modified_4 = 0.33;//new percentage 4  1 to which the gmlpo is modified

sinput group "- MDP/Maximum daily profit/Maxima ganancia diaria -"
input bool mdp_is_strict = true; //MDP is strict?
input double percentage_or_money_mdp_input = 0; //percentage or money (0 => not used MDP)
input ENUM_RISK_CALCULATION_MODE mode_calculation_mdp = percentage; //Mode calculation Max Daily Profit
input ENUM_APPLIED_PERCENTAGES applied_percentages_mdp = Balance;//MDP percentage applies to:

sinput group "--- Session ---"
input         char hora_inicio = 16;//start hour to operate (0-23)
input         char min_inicio = 30;//start minute to operate (0-59)
input         char hora_fin = 18;//end hour to operate (1-23)
input         char min_fin =0;//end minute to operate (0-59)


グローバル変数の宣言

このセクションでは、EAのリスク管理と取引動作を制御するための各種グローバル変数を作成します。

1. CTradeおよびCRiskManagementオブジェクト

取引処理を実行するために、本連載で作成したRisk_Management.mqhライブラリをインクルードします。あわせて、CTrade型のオブジェクトを宣言します。

#include  <Risk_Management.mqh>
CTrade trade;

続いて、CRiskManagementクラスのインスタンスを生成し、コンストラクタに必要なパラメータを設定します。

CRiskManagemet risk(mdp_is_strict, get_mode, Magic, risk_mode, prop_firm_balance);

2. インジケーターディスクリプタ格納用変数

EAの判断ロジックを可視化するために、2つのインジケーターディスクリプタを保持する変数を定義します。1つはOrder Blocksインジケーター用、もう1つはEMA用です。

//--- Handles
int order_block_indicator_handle;
int hanlde_ma;

3. TP値とSL値を格納するための配列

Order Blocksインジケーターのバッファからコピーした値を保存するため、4つの配列を定義します。

double tp1[];
double tp2[];
double sl1[];
double sl2[];

4. 過去の終値に関する変数(日次、週次、およびOrder Blocksの期間)

日次、週次、またはオーダーブロックが検出された時間足における最新バーのタイムスタンプ(決済時刻)を保持するための変数を定義します。

//---
datetime TiempoBarraApertua;
datetime TiempoBarraApertua_1;
datetime prev_vela;

5. 取引活動を有効/無効にするためのブール変数

一定の利益または損失制限を超えた後にEAの動作を制御するため、取引許可状態を保持するブール変数を作成します。trueの場合は取引可能、falseの場合は取引停止となります。

//---
bool opera = true;

6. 取引セッションの開始と終了を保存するための変数

最後に、売買シグナルを探索する取引セッションの開始時刻および終了時刻を定義する変数を用意します。

datetime start_sesion;
datetime end_sesion;


OnInit関数

このセクションでは、EAが正しく動作するために必要なすべての設定をおこないます。Order Blocksインジケーターのパラメータを宣言、設定し、そのディスクリプタとEMAを初期化したうえで、最終的にリスク管理システムを構成します。

1. Order Blocksインジケーター用のMqlParam配列の作成

最初のステップとして、各インジケーター設定に対応するパラメータ配列を準備します。MqlParam構造体を使用することで、これらのパラメータをIndicatorCreate()関数へ順序通りに渡すことができます。

//---
  MqlParam param[17];

  param[0].type = TYPE_STRING;
  param[0].string_value = "::Indicators\\Order_Block_Indicador_New_Part_2";

  param[1].type = TYPE_STRING;
  param[1].string_value = "--- Order Block Indicator settings ---";

  param[2].type = TYPE_STRING;
  param[2].string_value = "-- Order Block --";

  param[3].type = TYPE_INT;
  param[3].integer_value = Rango_universal_busqueda;

  param[4].type = TYPE_INT;
  param[4].integer_value = Witdth_order_block;

  param[5].type = TYPE_BOOL;
  param[5].integer_value = Back_order_block;

  param[6].type = TYPE_BOOL;
  param[6].integer_value = Fill_order_block;

  param[7].type = TYPE_COLOR;
  param[7].integer_value = Color_Order_Block_Bajista;

  param[8].type = TYPE_COLOR;
  param[8].integer_value = Color_Order_Block_Alcista;

  param[9].type = TYPE_STRING;
  param[9].string_value = "-- Strategy --";

  param[10].type = TYPE_INT;
  param[10].integer_value = tp_sl_style;

  param[11].type = TYPE_STRING;
  param[11].string_value = "- ATR";

  param[12].type = TYPE_DOUBLE;
  param[12].double_value = Atr_Multiplier_1;

  param[13].type = TYPE_DOUBLE;
  param[13].double_value = Atr_Multiplier_2;

  param[14].type = TYPE_STRING;
  param[14].string_value = "- POINT";

  param[15].type = TYPE_INT;
  param[15].integer_value = TP_POINT;

  param[16].type = TYPE_INT;
  param[16].integer_value = SL_POINT;

param[]配列の各インデックスは、Order Blocksインジケーターの動作に必要な各パラメータに対応しています。

2. インジケーターディスクリプタの作成と検証

配列の設定が完了した後、IndicatorCreate()関数を使用してOrder Blocksインジケーターのディスクリプタを取得します。あわせて、EMAのディスクリプタも作成し、エントリーおよびエグジット戦略における補助的な判断材料として使用します。

//---
  order_block_indicator_handle = IndicatorCreate(_Symbol, timeframe_order_block, IND_CUSTOM, ArraySize(param), param);
  hanlde_ma = iMA(_Symbol, timeframe_order_block, 30, 0, MODE_EMA, PRICE_CLOSE);
  trade.SetExpertMagicNumber(Magic);

  if(order_block_indicator_handle == INVALID_HANDLE)
   {
    Print("The order blocks indicator is not available last error: ", _LastError);
    return INIT_FAILED;
   }

  if(hanlde_ma == INVALID_HANDLE)
   {
    Print("The ema indicator is not available latest error: ", _LastError);
    return INIT_FAILED;
   }

3. インジケーターのチャートへの追加

デバッグおよび視覚的な監視を簡単にするために、インジケーターをチャートへ直接追加することができます。このステップは任意ですが、チャート上でオブジェクトを確認できることで、パラメータ設定や表示内容が正しいかどうかを検証しやすくなります。

  ChartIndicatorAdd(0, 0, order_block_indicator_handle);
  ChartIndicatorAdd(0, 0, hanlde_ma);

4. リスク管理の設定

損失および利益の制限の適用、ならびに最適なロットサイズの計算を担当するCRiskManagementオブジェクトを設定します。

//---
  risk.SetPorcentages(percentage_or_money_mdl_input, percentage_or_money_mwl_input, percentage_or_money_gmlpo_input
                      , percentage_or_money_ml_input, percentage_or_money_mdp_input);
  risk.SetEnums(mode_calculation_mdl, mode_calculation_mwl, mode_calculation_gmlpo, mode_calculation_ml, mode_calculation_mdp);
  risk.SetApplieds(applied_percentages_mdl, applied_percentages_mwl, applied_percentages_gmlpo, applied_percentages_ml, applied_percentages_mdp);

動的GMLPO構成

選択された設定モード(固定または完全カスタマイズ)に応じて、1取引あたりのリスク値を設定します。

  if(mode_gmlpo == DYNAMIC_GMLPO_FIXED_PARAMETERS)
   {
    string percentages_to_activate, risks_to_be_applied;
    SetDynamicGMLPOUsingFixedParameters(inp_balance_percentage_to_activate_the_risk_1, inp_balance_percentage_to_activate_the_risk_2, inp_balance_percentage_to_activate_the_risk_3
                                        , inp_balance_percentage_to_activate_the_risk_4, inp_percentage_to_be_modified_1, inp_percentage_to_be_modified_2, inp_percentage_to_be_modified_3, inp_percentage_to_be_modified_4
                                        , percentages_to_activate, risks_to_be_applied);
    risk.SetDynamicGMLPO(percentages_to_activate, risks_to_be_applied, revision_type_gmlpo);
   }
  else
    if(mode_gmlpo == DYNAMIC_GMLPO_FULL_CUSTOM)
      risk.SetDynamicGMLPO(str_percentages_to_be_reviewed, str_percentages_to_apply, revision_type_gmlpo);

SetDynamicGMLPOUsingFixedParameters()関数は、固定パラメータ(inp_balance_percentage_to_activate_the_risk_Xおよびinp_percentage_to_be_modified_X)を文字列に変換します。この関数は非常にシンプルです。基本的には、文字列に変換された変数の値を連結して文字列を作成します。

void SetDynamicGMLPOUsingFixedParameters(
  double _balance_percentage_to_activate_the_risk_1, double _balance_percentage_to_activate_the_risk_2, double _balance_percentage_to_activate_the_risk_3, double _balance_percentage_to_activate_the_risk_4,
  double _percentage_to_be_modified_1, double _percentage_to_be_modified_2, double _percentage_to_be_modified_3, double _percentage_to_be_modified_4,
  string &percentages_to_activate, string &risks_to_be_applied)
 {
  percentages_to_activate = DoubleToString(_balance_percentage_to_activate_the_risk_1) + "," + DoubleToString(_balance_percentage_to_activate_the_risk_2) + "," + DoubleToString(_balance_percentage_to_activate_the_risk_3)
                            + "," + DoubleToString(_balance_percentage_to_activate_the_risk_4);
  risks_to_be_applied = DoubleToString(_percentage_to_be_modified_1) + "," + DoubleToString(_percentage_to_be_modified_2) + "," + DoubleToString(_percentage_to_be_modified_3)
                        + "," + DoubleToString(_percentage_to_be_modified_4);
 }

 5. Order Blocksインジケーター用TP/SL配列の設定

最後に、Order Blocksインジケーターのデータを順次格納するための配列を設定します。

//---
  ArraySetAsSeries(tp1, true);
  ArraySetAsSeries(tp2, true);
  ArraySetAsSeries(sl1, true);
  ArraySetAsSeries(sl2, true);


OnTick関数とOnTradeTransaction関数

OnTickおよびOnTradeTransaction関数は、あらゆる取引システムにおいて重要な役割を持ちます。本システムでは、OnTickを使用して損失制限の超過判定、新しい日足・週足の検出、ならびにOrder Blocksインジケーターのデータに基づくシグナル実行をおこないます。

1. 新しい日と週の確認、セッション設定

OnTick内の最初の処理として、新しい日または新しい週が開始されたかどうかを判定します。これは、日足(PERIOD_D1)および週足(PERIOD_W1)の最新バーの日時を比較することでおこないます。

日足のチェック処理では、opera変数をtrueにリセットします(これは取引が許可されている状態を示します)。さらに、riskオブジェクトのOnNewDay関数を呼び出し、その日の取引セッション時間を計算します。

//---
  if(TiempoBarraApertua != iTime(_Symbol, PERIOD_D1, 0))
   {
    opera = true;
    risk.OnNewDay();
    start_sesion = HoraYMinutoADatetime(hora_inicio,min_inicio);
    end_sesion = HoraYMinutoADatetime(hora_fin,min_fin);

    if(TiempoBarraApertua_1 != iTime(_Symbol, PERIOD_W1, 0))
     {
      risk.OnNewWeek();
      TiempoBarraApertua_1 = iTime(_Symbol, PERIOD_W1, 0);
     }

    TiempoBarraApertua = iTime(_Symbol, PERIOD_D1, 0);
   }

注記: HoraYMinutoADatetime(int hora, int minuto)は、入力パラメータとして設定された時および分をdatetime型の変数に変換する関数です。

datetime HoraYMinutoADatetime(int hora, int minuto) {
  MqlDateTime tm;
  TimeCurrent(tm);
// Asigna la hora y el minuto deseado
  tm.hour = hora;
  tm.min = minuto;
  tm.sec = 0; // Puedes ajustar los segundos si es necesario
  return StructToTime(tm);;
}

2. Order Blocksの時間足における新規ローソク足ごとのチェックとロジック

次のステップでは、Order Blocksインジケーターに設定された時間足で新しいバーが形成されたかどうかを確認します。新しいローソク足が検出された後、インジケーターバッファをコピーしてTPおよびSLの値を取得します。続いて、ロットサイズ計算のためにSLを設定し、シグナルに基づいて買いまたは売り注文を実行します。

 if(prev_vela != iTime(_Symbol, timeframe_order_block, 0))
   {
    CopyBuffer(order_block_indicator_handle, 2, 0, 5, tp1);
    CopyBuffer(order_block_indicator_handle, 3, 0, 5, tp2);
    CopyBuffer(order_block_indicator_handle, 4, 0, 5, sl1);
    CopyBuffer(order_block_indicator_handle, 5, 0, 5, sl2);

    if(tp1[0] > 0 && tp2[0]  > 0 && sl1[0] > 0 &&  sl2[0] > 0)
     {
      if(tp2[0] > sl2[0] && risk.GetPositionsTotal() == 0)  //compras
       {
        double ASK = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits);
        risk.SetStopLoss(ASK - sl1[0]);
        double lot = (Lote_Type == Dinamico ? risk.GetLote(ORDER_TYPE_BUY) : lote);

        if(lot > 0.0)
          trade.Buy(lot, _Symbol, ASK, sl1[0], tp1[0], "Order Block EA Buy");
       }
      else
        if(sl2[0] > tp2[0] && risk.GetPositionsTotal() == 0)  //venta
         {
          double BID = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits);
          risk.SetStopLoss(sl1[0] - BID);
          double lot = (Lote_Type == Dinamico ? risk.GetLote(ORDER_TYPE_SELL) : lote);

          if(lot > 0.0)
            trade.Sell(lot, _Symbol, BID, sl1[0], tp1[0], "Order Block EA Sell");
         }
     }

    prev_vela = iTime(_Symbol, timeframe_order_block, 0);
   }

注記:risk.GetPositionsTotal()は、同時に保有できるポジション数を制限します。本例では、新規注文を出す前にポジションが一切開かれていないことを確認しています。

sl1およびtp1の値が使用されますが、パラメータ設定によってはtp2を使用して、1:2など異なるリスクリワード比を設定することも可能です。

3. 最終リスク管理チェック

各OnTickの最後に、損失または利益の制限を超過しているかどうかを確認します。制限を超えている場合、EAによって開かれたすべてのポジションをクローズし、opera変数の値をfalseに設定します。

 risk.OnTickEvent();

  if(risk.ML_IsSuperated(CLOSE_POSITION_AND_EQUITY)  == true)
   {
    if(risk_mode == propfirm_ftmo)
     {
      Print("The expert advisor lost the funding test");
      ExpertRemove();
     }
    else
     {
      risk.CloseAllPositions();
      Print("Maximum loss exceeded now");
      opera = false;
     }
   }

  if(risk.MDL_IsSuperated(CLOSE_POSITION_AND_EQUITY) == true)
   {
    risk.CloseAllPositions();
    Print("Maximum daily loss exceeded now");
    opera = false;
   }

  if(risk.MDP_IsSuperated(CLOSE_POSITION_AND_EQUITY) == true)
   {
    risk.CloseAllPositions();
    Print("Excellent Maximum daily profit achieved");
    opera = false;
   }

注記: 追加のチェックとして、最大週次損失など他の損失タイプを監視する処理を追加することも可能です。

 if(risk.MWL_IsSuperated(CLOSE_POSITION_AND_EQUITY) == true)
   {
    risk.CloseAllPositions();
    Print("The maximum weekly loss has been exceeded");
    opera = false;
    extra = false;
   }

4. OnTradeTransactionイベント

最後に、ポジションの新規オープン、決済、変更イベントを受け取るためにOnTradeTransaction関数を実装します。この関数では、これらのイベントを処理するためにリスク管理クラスの対応するメソッドを呼び出します。

//+------------------------------------------------------------------+
//| TradeTransaction function                                        |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction& trans,
                        const MqlTradeRequest& request,
                        const MqlTradeResult& result)
 {
  risk.OnTradeTransactionEvent(trans);
 }


OnDeinit関数

OnDeinit関数は、EAがチャートから削除される、または接続が解除される直前に呼び出されます。この段階では、すべてのリソースを解放し、実行中に追加されたインジケーターやオブジェクトを削除して、チャートをクリーンな状態に保ち、メモリリークを防ぐことが推奨されます。

以下に、このクリーンアップ処理の例を示します。

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
 {
//---
  ChartIndicatorDelete(0, 0, ChartIndicatorName(0, 0, GetMovingAverageIndex()));
  ChartIndicatorDelete(0, 0, "Order Block Indicator");

  if(hanlde_ma != INVALID_HANDLE)
    IndicatorRelease(hanlde_ma);

  if(order_block_indicator_handle != INVALID_HANDLE)
    IndicatorRelease(order_block_indicator_handle);

 }

チャートから指数移動平均線(EMA)を見つけて削除する

移動平均線インジケーターを削除するには、まずその名前、つまりチャートの[インジケーター]タブに表示されている名前を見つける必要があります。場合によっては、これを実現する唯一の方法は、インジケーター名を順に調べて、名前全体またはその一部を検索することです。

//+------------------------------------------------------------------+
//| Extra Functions                                                  |
//+------------------------------------------------------------------+
int GetMovingAverageIndex(long chart_id = 0)
 {
  int total_indicators = ChartIndicatorsTotal(chart_id, 0);
  for(int i = 0; i < total_indicators; i++)
   {
    string indicator_name = ChartIndicatorName(chart_id, 0, i);
    if(StringFind(indicator_name, "MA") >= 0)  return i;
   }
  return -1;
 }
//+------------------------------------------------------------------+

ChartIndicatorsTotal(chart_id, 0): チャートのメインウィンドウに表示されているインジケーターの総数を返します。

ChartIndicatorName(chart_id, 0, i): チャートのメインウィンドウに表示されている各インジケーターの名前を返します。

StringFind(indicator_name, "MA"): 名前に「MA」が含まれているかどうかを確認します(「EMA」、「MA」など)。一致が見つかった場合、関数はそのインデックスを返します。

インジケーターリストにインデックスが登録されたら、その名前を使ってChartIndicatorDeleteでインジケーターを削除できます。

インジケーターディスクリプタの解放

IndicatorRelease()を呼び出すことで、特にカスタムインジケーターやディスクリプタ経由でデータにアクセスしている場合に、インジケーターをメモリから完全に解放することができます。これをおこなわない場合、EA終了後もメモリ内にデータが残る可能性があります。
  • handle_ma: 指数移動平均(EMA)のディスクリプタ
  • order_block_indicator_handle Order Blocksインジケーターのディスクリプタ


テスト

最後に、このEAをテストし、リスク管理システムを使用した場合のメリットとデメリットを評価します。

最初のバックテストでは、過去1年のデータを使用します。

テスト期間は2024年1月1日から2025年3月28日までです。

  • 銘柄:ゴールド
  • 時間足:M5
  • モデル:実ティックに基づいて各ティックで実行
  • レバレッジ:1:100

    設定:

    チャート:

                                                                                                           バックテスト1

    注記: このバックテストは最適化されていません。

    次に、EAを最適化します。以下にその結果を示します。

    • 新しい時間足:M3
    • 使用パラメータ:最大日次利益および最大日次損失

                                                                                                          バックテスト2       

    取引時間、最大損失、最大利益、1取引あたりのリスク、ATR倍率、および時間足(理想的には3分足)を最適化しています。

    注記:どちらのバックテストでも、ストップロスとテイクプロフィットの計算にはATRを使用しています。

    最後に、リスク管理がバックテスト結果に影響を与えることを示すため、最大日次損失および最大日次利益パラメータを削除し、それ以外の条件は維持します。

    以下がそのチャートです。

                                                                                                      バックテスト3                                                   

    ご覧の通り、バックテスト3では初期段階の成長は速いものの、その後さまざまな要因の影響を受けて下落傾向に転じています。一方、バックテスト2では急激なピークがなく、安定したスムーズな成長が見られます。これは、利益制限を設定することで過剰な取引を防ぎ、ドローダウンや予期しない状況から保護できることを示唆しています。また、最大損失制限は連敗時などに発生する潜在的損失を抑制する役割を持ちます。さらに、最大日次損失はEAを損益分岐点付近へ戻す効果があります。

    最後のテストでは、バックテスト3と同じ設定を使用しつつ、口座がマイナスになった時点で動的リスクを有効化します。

                                                                                                    バックテスト4  

    口座利益がマイナスの状態では、1取引あたりのリスクが大幅に低下し、潜在的な損失が制限されていることが確認できます。バックテスト3と4の主な違いは以下の通りです。

    • 利益局面

    バックテスト3では、利益局面において損失の回復だけでなく、口座全体が約10%増加しています。資金管理ルール(プロップファームの評価)では、この状態は不利となり、残高は約8,600ドルまで低下する結果になります。一方バックテスト4では、利益局面でも口座は約10,000ドル付近に維持され、大きな成長は発生していません。

    • 損失からの回復

    バックテスト4では、損失の回復により多くの時間とトレード数を要する傾向があります。ただし動的リスクの利点として、資金管理(プロップファーム評価)において口座はより安全に保護され、最低残高は約9,086ドル付近で維持されます。

    これらの結果から、動的リスクはプロップファームにおいて有利であると考えられます。潜在的損失を大幅に制限できるためです。一方で通常口座では、回復速度が遅くなる可能性があり、これは短期的利益を目的とする場合にはデメリットとなり得ますが、プロップファームでは「利益最大化」ではなく「口座維持」が目的であるため問題とはなりません。


    結論

    本記事では、Order BlocksインジケーターをベースとしたEAにリスク管理システムを実装しました。ご覧の通り、利益および損失の制限を使用する場合、また動的リスクを使用する場合では、結果に明確な違いが生じます。記事の最終部分では、システムの動作を可視化し、保護機構がどのように機能するかを実証しました。

    以下は、この記事で使用または更新されたファイルです。

    ファイル名 種類 説明 
    Risk_Management.mqh  .mqh(インクルードファイル) システム全体のリスク管理を担当するCRiskManagementクラスおよび共通関数を含む主要ファイル。このファイルでは利損管理に関するすべての機能を定義および拡張していますl。
    Order_Block_Indicador_New_Part_2.mq5 .mq5 Order Blocksインジケーターコードを含むファイル。
    Order Block EA MT5.mq5  .mq5 Order BlocksEAコードを含むファイル。
    Set 1% Risk.set  .set バックテスト1で使用した設定。
    Set Order Block EA.set .set バックテスト2で使用した設定(1日の最大損失額と利益額の制限を含む)。
    Set Dynamic Risk.set .set バックテスト3で使用した設定(動的リスクを有効化しつつ日次損失・利益制限は無効)。
    Set No Dynamic Risk.set .set バックテスト4で使用した設定(動的リスクおよび日次損失・利益制限はいずれも無効)

    MetaQuotes Ltdによりスペイン語から翻訳されました。
    元の記事: https://www.mql5.com/es/articles/17640

    添付されたファイル |
    MQL5.zip (36.61 KB)
    取引におけるニューラルネットワーク:周波数領域における異常検出(CATCH) 取引におけるニューラルネットワーク:周波数領域における異常検出(CATCH)
    CATCHフレームワークは、フーリエ変換と周波数パッチングを組み合わせることで、従来手法では捉えきれない市場異常を高精度に検出します。本記事では、このアプローチが金融データに潜む隠れたパターンをどのように明らかにするのかを解説します。
    初級から中級まで:構造体(III) 初級から中級まで:構造体(III)
    本記事では、「構造化されたコード」とは何かについて解説します。多くの人が「構造化されたコード」と「整理されたコード」を混同していますが、この2つの概念には明確な違いがあります。本記事ではその違いについて説明します。一見すると複雑に感じられるかもしれませんが、できるだけシンプルに理解できるように解説しています。ただし、本記事はより大きな内容へ進むための第一歩にすぎません。
    市場シミュレーション(第15回):ソケット(IX) 市場シミュレーション(第15回):ソケット(IX)
    本記事では、これまで実演してきた内容、すなわち「ExcelユーザーがMetaTrader 5上で操作できるようにする方法」の一例について解説します。ここで扱うのは、注文送信やポジションの新規建て・決済をExcel側から直接実行する方法ではなく、ExcelからMetaTrader 5上のEAにそれらの操作を指示する方法です。ユーザーはExcelを用いて特定銘柄のファンダメンタル分析をおこない、その結果をもとに、Excelだけを使ってMetaTrader 5上で稼働しているエキスパートアドバイザー(EA)に対し、特定ポジションの新規建てまたは決済を指示できるようにします。
    取引におけるニューラルネットワーク:多変量時系列のデュアルクラスタリング(最終回) 取引におけるニューラルネットワーク:多変量時系列のデュアルクラスタリング(最終回)
    DUETフレームワークの著者らによって提案されたアプローチの実装を引き続き進めます。本フレームワークは、時間方向とチャネル方向のクラスタリングを組み合わせることで、時系列データに潜在するパターンを抽出する革新的な手法を提供します。