English Русский 中文 Español Deutsch Português
preview
Candlestick Trend Constraintモデルの構築(第8回):エキスパートアドバイザーの開発 (I)

Candlestick Trend Constraintモデルの構築(第8回):エキスパートアドバイザーの開発 (I)

MetaTrader 5テスター | 18 10月 2024, 15:54
230 0
Clemence Benjamin
Clemence Benjamin

内容


はじめに

MetaEditorソフトウェアには、プロファイリング中に検出されたエラーを効果的に管理するコンパイラが含まれています。このツールのおかげで、旧バージョンでリスクとリターンの矩形が意図したとおりに表示されなかった原因を突き止めることができました。プログラムは正常にコンパイルされましたが、問題はコードそのものにあったわけではありません。その代わりに、主に特定のテクニカルのために、振り返りローソク足の範囲内に何も表示されなかったという事実に課題がありました。
  1. 振り返りローソク足の値がデフォルトで5,000本と高く設定されすぎていた
  2. 1つのプログラムに複数のバッファがあったため、計算が複雑になり、指標チャートウィンドウの表示が遅くなった

    遭遇した問題の解決方法について簡単に説明した後、この記事の主な目標である、改良されたTrend Constraint指標に基づいたエキスパートアドバイザーの開発に移ります。以下は、メイン指標で当初解決しようとしていた問題が別のスクリプトによってどのように解決されたかを示した画像です。

    移動平均線クロスオーバー時のリスクリワードレシオ

    矩形を使用して自動的に描画されるリスクとリターンの矩形


    リスクとリターンの矩形を描くことに関する先行課題の解決策

    次のように指標プログラムの課題に対処します。

    1. ルックバック期間を5000バーから1000バーに短縮して、計算するデータ量を大幅に減少しました。
    2. また、プログラムの負荷を軽減するために、ツールセットの一部としてスタンドアローンのスクリプトを作成しました。このスクリプトは、指標内のバッファ6とバッファ7が処理する条件を特に確認し、それらが満たされた場合、スクリプトは必要なリスクとリターンの矩形を描画します。さらに、エントリ価格、ストップロス、テイクプロフィットの各価格レベルを示すラインを追加します。このスクリプトは1回だけ実行されるものであり、継続的に動作するものではないため、ユーザーは手動でチャートにスクリプトを追加し、描画されたオブジェクトと価格マークによって表される取引レベルを確認する必要があります。

    下の画像は、スクリプトを起動した際の画面例です。

    Trend Constraint  R-Rスクリプトの起動

    Trend Constraint R-Rスクリプト:移動平均線がクロスオーバーしたときのリスクとリターンの矩形の描画

    この機能を分離することで、コンピュータや取引端末がフリーズすることなく、指標がスムーズに動作するようにしました。リスクとリターンの矩形を組み込み、エグジットレベルをマークすることで、トレーダーは取引の方向性とターゲットを事前に視覚的に評価することができ、EAを使わなくても手動取引が可能になります。必要なロジックを備えたスクリプトプログラムは完璧に機能しました。以下がスクリプトの全プログラムです。

    //+------------------------------------------------------------------+
    //|                                        Trend Constraint R-R.mq5  |
    //|                                                  Script program  |
    //+------------------------------------------------------------------+
    #property strict
    #property script_show_inputs
    #property copyright "2024 Clemence Benjamin"
    #property version "1.00"
    #property link "https://www.mql5.com/ja/users/billionaire2024/seller"
    #property description "A script program for drawing risk and rewars rectangles based on Moving Averaage crossover."
    
    
    
    //--- input parameters
    input int FastMAPeriod = 14;
    input int SlowMAPeriod = 50;
    input double RiskHeightPoints = 5000.0; // Default height of the risk rectangle in points
    input double RewardHeightPoints = 15000.0; // Default height of the reward rectangle in points
    input color RiskColor = clrIndianRed; // Default risk color
    input color RewardColor = clrSpringGreen; // Default reward color
    input int MaxBars = 500; // Maximum bars to process
    input int RectangleWidth = 10; // Width of the rectangle in bars
    input bool FillRectangles = true; // Option to fill rectangles
    input int FillTransparency = 128; // Transparency level (0-255), 128 is 50% transparency
    
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
    {
      //--- delete existing rectangles and lines
      DeleteExistingObjects();
    
      //--- declare and initialize variables
      int i, limit;
      double FastMA[], SlowMA[];
      double closePrice, riskLevel, rewardLevel;
    
      //--- calculate moving averages
      if (iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0 || iMA(NULL, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0)
      {
        Print("Error in calculating moving averages.");
        return;
      }
    
      ArraySetAsSeries(FastMA, true);
      ArraySetAsSeries(SlowMA, true);
    
      CopyBuffer(iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE), 0, 0, MaxBars, FastMA);
      CopyBuffer(iMA(NULL, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE), 0, 0, MaxBars, SlowMA);
    
      limit = MathMin(ArraySize(FastMA), ArraySize(SlowMA));
    
      for (i = 1; i < limit - 1; i++)
      {
        //--- check for crossover
        if (FastMA[i] > SlowMA[i] && FastMA[i - 1] <= SlowMA[i - 1])
        {
          //--- long position entry point (bullish crossover)
          closePrice = iClose(NULL, 0, i);
          riskLevel = closePrice + RiskHeightPoints * Point();
          rewardLevel = closePrice - RewardHeightPoints * Point();
    
          //--- draw risk rectangle
          DrawRectangle("Risk_" + IntegerToString(i), i, closePrice, i - RectangleWidth, riskLevel, RiskColor);
    
          //--- draw reward rectangle
          DrawRectangle("Reward_" + IntegerToString(i), i, closePrice, i - RectangleWidth, rewardLevel, RewardColor);
    
          //--- draw entry, stop loss, and take profit lines
          DrawPriceLine("Entry_" + IntegerToString(i), i, closePrice, clrBlue, "Entry: " + DoubleToString(closePrice, _Digits));
          DrawPriceLine("StopLoss_" + IntegerToString(i), i, riskLevel, clrRed, "Stop Loss: " + DoubleToString(riskLevel, _Digits));
          DrawPriceLine("TakeProfit_" + IntegerToString(i), i, rewardLevel, clrGreen, "Take Profit: " + DoubleToString(rewardLevel, _Digits));
        }
        else if (FastMA[i] < SlowMA[i] && FastMA[i - 1] >= SlowMA[i - 1])
        {
          //--- short position entry point (bearish crossover)
          closePrice = iClose(NULL, 0, i);
          riskLevel = closePrice - RiskHeightPoints * Point();
          rewardLevel = closePrice + RewardHeightPoints * Point();
    
          //--- draw risk rectangle
          DrawRectangle("Risk_" + IntegerToString(i), i, closePrice, i - RectangleWidth, riskLevel, RiskColor);
    
          //--- draw reward rectangle
          DrawRectangle("Reward_" + IntegerToString(i), i, closePrice, i - RectangleWidth, rewardLevel, RewardColor);
    
          //--- draw entry, stop loss, and take profit lines
          DrawPriceLine("Entry_" + IntegerToString(i), i, closePrice, clrBlue, "Entry: " + DoubleToString(closePrice, _Digits));
          DrawPriceLine("StopLoss_" + IntegerToString(i), i, riskLevel, clrRed, "Stop Loss: " + DoubleToString(riskLevel, _Digits));
          DrawPriceLine("TakeProfit_" + IntegerToString(i), i, rewardLevel, clrGreen, "Take Profit: " + DoubleToString(rewardLevel, _Digits));
        }
      }
    }
    
    //+------------------------------------------------------------------+
    //| Function to delete existing rectangles and lines                 |
    //+------------------------------------------------------------------+
    void DeleteExistingObjects()
    {
      int totalObjects = ObjectsTotal(0, 0, -1);
      for (int i = totalObjects - 1; i >= 0; i--)
      {
        string name = ObjectName(0, i, 0, -1);
        if (StringFind(name, "Risk_") >= 0 || StringFind(name, "Reward_") >= 0 ||
            StringFind(name, "Entry_") >= 0 || StringFind(name, "StopLoss_") >= 0 ||
            StringFind(name, "TakeProfit_") >= 0)
        {
          ObjectDelete(0, name);
        }
      }
    }
    
    //+------------------------------------------------------------------+
    //| Function to draw rectangles                                      |
    //+------------------------------------------------------------------+
    void DrawRectangle(string name, int startBar, double startPrice, int endBar, double endPrice, color rectColor)
    {
      if (ObjectFind(0, name) >= 0)
        ObjectDelete(0, name);
    
      datetime startTime = iTime(NULL, 0, startBar);
      datetime endTime = (endBar < 0) ? (TimeCurrent() + (PeriodSeconds() * (-endBar))) : iTime(NULL, 0, endBar);
    
      if (!ObjectCreate(0, name, OBJ_RECTANGLE, 0, startTime, startPrice, endTime, endPrice))
        Print("Failed to create rectangle: ", name);
    
      // Set the color with transparency (alpha value)
      int alphaValue = FillTransparency; // Adjust transparency level (0-255)
      color fillColor = rectColor & 0x00FFFFFF | (alphaValue << 24); // Combine alpha with RGB
    
      ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor);
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
      ObjectSetInteger(0, name, OBJPROP_BACK, true); // Set to background
    
      if (FillRectangles)
      {
        ObjectSetInteger(0, name, OBJPROP_COLOR, fillColor); // Fill color with transparency
      }
      else
      {
        ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor & 0x00FFFFFF); // No fill color
      }
    }
    
    //+------------------------------------------------------------------+
    //| Function to draw price lines                                     |
    //+------------------------------------------------------------------+
    void DrawPriceLine(string name, int barIndex, double price, color lineColor, string labelText)
    {
      datetime time = iTime(NULL, 0, barIndex);
      datetime endTime = (barIndex - 2 * RectangleWidth < 0) ? (TimeCurrent() + (PeriodSeconds() * (-barIndex - 2 * RectangleWidth))) : iTime(NULL, 0, barIndex - 2 * RectangleWidth); // Extend line to the right
    
      if (!ObjectCreate(0, name, OBJ_TREND, 0, time, price, endTime, price))
        Print("Failed to create price line: ", name);
    
      ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor);
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
      ObjectSetInteger(0, name, OBJPROP_BACK, true); // Set to background
    
      // Create text label
      string labelName = name + "_Label";
      if (ObjectFind(0, labelName) >= 0)
        ObjectDelete(0, labelName);
    
      if (!ObjectCreate(0, labelName, OBJ_TEXT, 0, endTime, price))
        Print("Failed to create label: ", labelName);
    
      ObjectSetInteger(0, labelName, OBJPROP_COLOR, lineColor);
      ObjectSetInteger(0, labelName, OBJPROP_ANCHOR, ANCHOR_LEFT);
      ObjectSetString(0, labelName, OBJPROP_TEXT, labelText);
      ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 10);
      ObjectSetInteger(0, labelName, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
      ObjectSetInteger(0, labelName, OBJPROP_XOFFSET, 5);
      ObjectSetInteger(0, labelName, OBJPROP_YOFFSET, 0);
    }

    EAの開発を進める前に、スクリプトの性能について徹底的に議論しましょう。

    • 入力パラメータを定義することで、取引戦略の主要な側面をカスタマイズできるようにしました。具体的には、高速移動平均と低速移動平均の期間の調整、リスクとリターンの矩形の寸法や色の設定、処理するバーの最大数の指定、矩形を塗りつぶすかどうかの選択が可能です。これにより、ユーザーの好みに合わせたさまざまな取引戦略に対応できるようになり、プログラムの入力パラメータセクションが形成されました。

    //---- Input Parameters
    input int FastMAPeriod = 14;
    input int SlowMAPeriod = 50;
    input double RiskHeightPoints = 5000.0;
    input double RewardHeightPoints = 15000.0;
    input color RiskColor = clrIndianRed;
    input color RewardColor = clrSpringGreen;
    input int MaxBars = 500;
    input int RectangleWidth = 10;
    input bool FillRectangles = true;
    input int FillTransparency = 128;

    • OnStart関数では、まず既存のリスクとリターンの矩形および価格ラインを削除するようにスクリプトを設計し、チャートが常にクリーンな状態で保たれるようにしました。次に、iMA関数を使用して高速移動平均と低速移動平均を計算し、それらの値をさらなる処理のために配列に格納しました。スクリプトがチャート上のバーをループする際、動きの速い平均が動きの遅い平均を上回る「強気のクロスオーバー」を検出する条件を設定しました。これらの条件が満たされると、エントリ価格、リスクレベル(ストップロス)、リワードレベル(テイクプロフィット)を計算し、チャートに矩形と価格ラインをを描画して、これらの重要な取引レベルを視覚的に示すようにしました。次に、これに関連するサブコードスニペットについて説明します。

    //----Onstart Function
    void OnStart()
    {
      //--- delete existing rectangles and lines
      DeleteExistingObjects();
    
      //--- declare and initialize variables
      int i, limit;
      double FastMA[], SlowMA[];
      double closePrice, riskLevel, rewardLevel;
    
      //--- calculate moving averages
      if (iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0 || iMA(NULL, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0)
      {
        Print("Error in calculating moving averages.");
        return;
      }
    

    • チャートの明瞭さを保つために、DeleteExistingObjects関数を作成しました。この関数は、スクリプトが以前に描画した売買シグナルに関連するオブジェクトを削除する役割を果たします。スクリプトは、チャート上のすべてのオブジェクトの名前を確認し、不要なものを削除することで、最新の関連情報のみが表示されるようにします。これにより、チャートの見やすさが向上し、不要な情報による混乱が排除されました。

    //---- DeleteAllExistingObjects Function
    void DeleteExistingObjects()
    {
      int totalObjects = ObjectsTotal(0, 0, -1);
      for (int i = totalObjects - 1; i >= 0; i--)
      {
        string name = ObjectName(0, i, 0, -1);
        if (StringFind(name, "Risk_") >= 0 || StringFind(name, "Reward_") >= 0 ||
            StringFind(name, "Entry_") >= 0 || StringFind(name, "StopLoss_") >= 0 ||
            StringFind(name, "TakeProfit_") >= 0)
        {
          ObjectDelete(0, name);
        }
      }
    }

    • DrawRectangle関数では、まず重複を避けるために、同じ名前の既存の矩形をすべて削除します。これにより、スクリプトがリスクとリワードのレベルを視覚的に表現できるようになります。次に、バーのインデックスに基づいて矩形の開始時間と終了時間を計算し、色と透明度を慎重に設定します。この工夫により、矩形は他の重要な詳細を隠すことなく、チャート上で際立つようになります。
    ///---Draw rectangle function
    void DrawRectangle(string name, int startBar, double startPrice, int endBar, double endPrice, color rectColor)
    {
      if (ObjectFind(0, name) >= 0)
        ObjectDelete(0, name);
    
      datetime startTime = iTime(NULL, 0, startBar);
      datetime endTime = (endBar < 0) ? (TimeCurrent() + (PeriodSeconds() * (-endBar))) : iTime(NULL, 0, endBar);
    
      if (!ObjectCreate(0, name, OBJ_RECTANGLE, 0, startTime, startPrice, endTime, endPrice))
        Print("Failed to create rectangle: ", name);
    
      int alphaValue = FillTransparency;
      color fillColor = rectColor & 0x00FFFFFF | (alphaValue << 24);
    
      ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor);
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
      ObjectSetInteger(0, name, OBJPROP_BACK, true);
    
      if (FillRectangles)
      {
        ObjectSetInteger(0, name, OBJPROP_COLOR, fillColor);
      }
      else
      {
        ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor & 0x00FFFFFF);
      }
    }

      • 最後に、スクリプトにエントリ、ストップロス、テイクプロフィットレベルに水平ラインを追加するよう指示するDrawPriceLine関数を実装しました。この関数により、スクリプトはこれらの線をチャート全体に伸ばし、対応する価格水準を表示するテキストラベルを追加します。これにより、ユーザーは移動平均線によって生成されたシグナルに基づき、取引のキーポイントを素早く特定し、管理することが可能になります。

      ///---- Draw Price Lines Function
      void DrawPriceLine(string name, int barIndex, double price, color lineColor, string labelText)
      {
        datetime time = iTime(NULL, 0, barIndex);
        datetime endTime = (barIndex - 2 * RectangleWidth < 0) ? (TimeCurrent() + (PeriodSeconds() * (-barIndex - 2 * RectangleWidth))) : iTime(NULL, 0, barIndex - 2 * RectangleWidth);
      
        if (!ObjectCreate(0, name, OBJ_TREND, 0, time, price, endTime, price))
          Print("Failed to create price line: ", name);
      
        ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor);
        ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
        ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
        ObjectSetInteger(0, name, OBJPROP_BACK, true);
      
        string labelName = name + "_Label";
        if (ObjectFind(0, labelName) >= 0)
          ObjectDelete(0, labelName);
      
        if (!ObjectCreate(0, labelName, OBJ_TEXT, 0, endTime, price))
          Print("Failed to create label: ", labelName);
      
        ObjectSetInteger(0, labelName, OBJPROP_COLOR, lineColor);
        ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 10);
        ObjectSetString(0, labelName, OBJPROP_TEXT, labelText);
        ObjectSetInteger(0, labelName, OBJPROP_BACK, true);
      }
      

      取引キットの一部として、このスクリプトを定期的に起動し、過去と現在の取引レベルを視覚化できます。次に、独自のEAを作成します。この記事では、最終的に作動するEAまでの開発全体を説明することに重点を置きます。特に、前回作成した指標「Trend Constraint V1.09」に沿って動作させることに焦点を当てます。


      指標に基づいて動作するEAを作成する

      カスタム指標を使用してMQL5 EAを作成するには、カスタム指標のファイル(.ex5)がMetaTrader 5プラットフォーム内のIndicatorsフォルダに存在していることを確認する必要があります。この場合はTrend Constraint V1.09です。MetaEditorを使用して、指標のバッファ値にアクセスするMQL5関数を組み込んだEAを作成します。EA内でカスタム指標を呼び出すために、iCustom()関数を用いて銘柄や時間枠などの必要なパラメータを指定します。

      指標バッファからデータを抽出するには、CopyBuffer()関数を使用します。この関数は、取引シグナルを分析するためのバッファ値を取得します。次に、これらのバッファ値に基づいて取引ロジックを実装し、戦略に従って注文をオープン、クローズ、または修正する条件を定義します。た、ストップロスやテイクプロフィットなどのリスク管理機能を統合し、慎重な取引管理を実現します。MetaTrader 5ストラテジーテスターを使用してEAをバックテストし、そのパフォーマンスを評価し、パラメータを微調整します。最後に、ライブ取引に移行する前に、デモ口座環境でEAの機能を検証し、実際の市場環境下で効果的に動作することを確認します。

      まずはMetaEditorでEAのテンプレートを起動し、この画像の例に基づいて開発や修正をおこないます。

      MetaEditorでEAテンプレートを起動する

      MetaEditorでEAテンプレートを起動する

      EAの構築手順を6つの段階に分けて説明します。この作業を進めるにあたり、コードスニペットを直接MetaEditorに入力することをお勧めします。この実践的なアプローチは、特にEA開発初心者にとっては、手順をよりよく理解し、内面化するのに役立ちます。

      1. ヘッダーとメタデータ

      ヘッダーセクションでは、EAの名前と目的を定義します。著作権情報、プロフィールへのリンク、バージョンを明記することで、EAを簡単に識別し、追跡できるようにしています。このメタデータは、特にEAが共有されたり変更されたりした場合に、自分や他のユーザーがEAの出所や目的を理解するのに役立ちます。

      //You can replace the author details with yours.
      //+------------------------------------------------------------------+
      //|                                    Trend Constraint Expert.mq5   |
      //|                                Copyright 2024, Clemence Benjamin |
      //|                                             https://www.mql5.com |
      //+------------------------------------------------------------------+
      #property strict
      #property copyright "Copyright 2024, Clemence Benjamin"
      #property link      "https://www.mql5.com/ja/users/billionaire2024/seller"
      #property version   "1.0"
      #property description "An Expert based on the buffer6 and buffer7 of Trend Constraint V1.09"

      2. 入力パラメータ

      ここでは、コードを変更することなくEAの動作をカスタマイズするための主要な入力パラメータを定義します。ロットサイズ、スリッページ、ストップロス、テイクプロフィット、マジックナンバーなどのパラメータを設定することで、EAの柔軟性が向上し、さまざまな取引戦略に適応できるようになります。このマジックナンバーは特に重要であり、このEAが実行した取引を一意に識別できるため、複数のEAや手動取引が関与する場合には非常に役立ちます。

      ///----------- EA inputs parameters for customizations
      input double Lots = 0.1;          // Lot size
      input int Slippage = 3;           // Slippage
      input double StopLoss = 50;       // Stop Loss in points
      input double TakeProfit = 100;    // Take Profit in points
      input int MagicNumber = 123456;   // Magic number for orders

      3. 初期化関数(OnInit)

      OnInit関数では、EAの動作の基盤を整えるために必要なコンポーネントを初期化します。まず、カスタム指標であるTrend Constraint V1.09のハンドルを取得します。このハンドルにより、プログラムが指標と対話できるようになります。ハンドルの取得に成功した場合、バッファ配列(Buffer6とBuffer7)を直列に設定し、指標値の保存と操作が可能になります。一方、ハンドルの取得に失敗した場合は、EAが初期化に失敗し、問題の診断に役立つエラーメッセージを返します。

      ////-------Initialization Function
      int OnInit()
        {
         //--- Get the indicator handle
         indicator_handle = iCustom(Symbol(), PERIOD_CURRENT, "Trend Constraint V1.09");
         if (indicator_handle < 0)
           {
            Print("Failed to get the indicator handle. Error: ", GetLastError());
            return(INIT_FAILED);
           }
      
         //--- Set the buffer arrays as series
         ArraySetAsSeries(Buffer6, true);
         ArraySetAsSeries(Buffer7, true);
      
         return(INIT_SUCCEEDED);
        }
      

      4. 初期化解除関数(OnDeinit)

      EAがチャートから削除されるか、プラットフォームが閉じられると、OnDeinit関数が実行されます。この関数では、EAの動作中に割り当てられたリソースを確実に解放するために、指標ハンドルを適切に解放します。このクリーンアップ手順は、不必要なリソースの消費を防ぎ、取引環境の効率性と安定性を維持するために非常に重要です。

      ///------Deinitialization Function(OnDeinit)
      void OnDeinit(const int reason)
        {
         //--- Release the indicator handle
         IndicatorRelease(indicator_handle);
        }
      

      5. メイン実行関数(OnTick)

      OnTick関数は、実際の取引アクションがおこなわれる場所です。この関数は、新しいマーケットティックを受信するたびに呼び出されます。まず、同じマジックナンバーのポジションがすでに存在するかどうかを確認し、重複した取引を避けます。次に、指標バッファ(Buffer6とBuffer7)から最新の値をコピーし、売買の意思決定をおこないます。売買シグナルの条件が満たされた場合、適切な取引リクエストを作成し、送信します。注文の種類、価格、ストップロス、テイクプロフィット、スリッページなど、必要なパラメータをすべて指定し、取引戦略を効果的に実行できるよう配慮しています。

      ///---Main Execution Function(OnTick)
      void OnTick()
        {
         //--- Check if there is already an open position with the same MagicNumber
         if (PositionSelect(Symbol()))
           {
            if (PositionGetInteger(POSITION_MAGIC) == MagicNumber)
              {
               return; // Exit OnTick if there's an open position with the same MagicNumber
              }
           }
      
         //--- Calculate the indicator
         if (CopyBuffer(indicator_handle, 5, 0, 2, Buffer6) <= 0 || CopyBuffer(indicator_handle, 6, 0, 2, Buffer7) <= 0)
           {
            Print("Failed to copy buffer values. Error: ", GetLastError());
            return;
           }
      
         //--- Check for a buy signal
         if (Buffer7[0] != EMPTY_VALUE)
           {
            double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
            double sl = NormalizeDouble(ask - StopLoss * _Point, _Digits);
            double tp = NormalizeDouble(ask + TakeProfit * _Point, _Digits);
      
            //--- Prepare the buy order request
            MqlTradeRequest request;
            MqlTradeResult result;
            ZeroMemory(request);
            ZeroMemory(result);
      
            request.action = TRADE_ACTION_DEAL;
            request.symbol = Symbol();
            request.volume = Lots;
            request.type = ORDER_TYPE_BUY;
            request.price = ask;
            request.sl = sl;
            request.tp = tp;
            request.deviation = Slippage;
            request.magic = MagicNumber;
            request.comment = "Buy Order";
      
            //--- Send the buy order
            if (!OrderSend(request, result))
              {
               Print("Error opening buy order: ", result.retcode);
              }
            else
              {
               Print("Buy order opened successfully! Ticket: ", result.order);
              }
           }
      
         //--- Check for a sell signal
         if (Buffer6[0] != EMPTY_VALUE)
           {
            double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
            double sl = NormalizeDouble(bid + StopLoss * _Point, _Digits);
            double tp = NormalizeDouble(bid - TakeProfit * _Point, _Digits);
      
            //--- Prepare the sell order request
            MqlTradeRequest request;
            MqlTradeResult result;
            ZeroMemory(request);
            ZeroMemory(result);
      
            request.action = TRADE_ACTION_DEAL;
            request.symbol = Symbol();
            request.volume = Lots;
            request.type = ORDER_TYPE_SELL;
            request.price = bid;
            request.sl = sl;
            request.tp = tp;
            request.deviation = Slippage;
            request.magic = MagicNumber;
            request.comment = "Sell Order";
      
            //--- Send the sell order
            if (!OrderSend(request, result))
              {
               Print("Error opening sell order: ", result.retcode);
              }
            else
              {
               Print("Sell order opened successfully! Ticket: ", result.order);
              }
           }
        }
      

      6. その他の関数

      EAが遭遇する可能性のあるさまざまなイベントを処理するために、いくつかの関数も用意されていますが、これらはEAテンプレートの一部として提供されたものであり、現在は簡略化のために使用していません。

      • OnTrade:ここでは、取引イベントが発生した際に必要な特定のアクションを処理できます。現在は空ですが、必要に応じてロジックを追加するためのスペースとして確保されています。
      • OnTester:この関数は、バックテスト中のカスタム計算に使用されます。値を返すことで、特定の指標に基づいて戦略を最適化できます。
      • OnTesterInit、OnTesterPass、OnTesterDeinit:これらの関数は、ストラテジーテスター内の最適化プロセスに関与しています。これにより、設定の初期化、最適化パス後のアクションの実行、最適化後のリソースのクリーンアップが可能となります。
      • OnChartEvent:この関数を使用すると、マウスのクリックやキーの押下など、さまざまなチャートイベントを処理できます。現在は空ですが、このスペースを利用してEAにインタラクティブな機能を追加することが可能です。
      ///----Other Template functions available
      void OnTrade()
        {
         //--- Handle trade events if necessary
        }
      
      double OnTester()
        {
         double ret = 0.0;
         //--- Custom calculations for strategy tester
         return (ret);
        }
      
      void OnTesterInit()
        {
         //--- Initialization for the strategy tester
        }
      
      void OnTesterPass()
        {
         //--- Code executed after each pass in optimization
        }
      
      void OnTesterDeinit()
        {
         //--- Cleanup after tester runs
        }
      
      void OnChartEvent(const int id,
                        const long &lparam,
                        const double &dparam,
                        const string &sparam)
        {
         //--- Handle chart events here
        }
      

      最終的なプログラムはこのようになります。

      //+------------------------------------------------------------------+
      //|                                    Trend Constraint Expert.mq5   |
      //|                                Copyright 2024, Clemence Benjamin |
      //|                                             https://www.mql5.com |
      //+------------------------------------------------------------------+
      #property strict
      #property copyright "Copyright 2024, Clemence Benjamin"
      #property link      "https://www.mql5.com/ja/users/billionaire2024/seller"
      #property version   "1.0"
      #property description "An Expert based on the buffer6 and buffer7 of Trend Constraint V1.09"
      
      //--- Input parameters for the EA
      input double Lots = 0.1;          // Lot size
      input int Slippage = 3;           // Slippage
      input double StopLoss = 50;       // Stop Loss in points
      input double TakeProfit = 100;    // Take Profit in points
      input int MagicNumber = 123456;   // Magic number for orders
      
      //--- Indicator handle
      int indicator_handle;
      double Buffer6[];
      double Buffer7[];
      
      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
        {
         //--- Get the indicator handle
         indicator_handle = iCustom(Symbol(), PERIOD_CURRENT, "Trend Constraint V1.09");
         if (indicator_handle < 0)
           {
            Print("Failed to get the indicator handle. Error: ", GetLastError());
            return(INIT_FAILED);
           }
      
         //--- Set the buffer arrays as series
         ArraySetAsSeries(Buffer6, true);
         ArraySetAsSeries(Buffer7, true);
      
         return(INIT_SUCCEEDED);
        }
      
      //+------------------------------------------------------------------+
      //| Expert deinitialization function                                 |
      //+------------------------------------------------------------------+
      void OnDeinit(const int reason)
        {
         //--- Release the indicator handle
         IndicatorRelease(indicator_handle);
        }
      
      //+------------------------------------------------------------------+
      //| Expert tick function                                             |
      //+------------------------------------------------------------------+
      void OnTick()
        {
         //--- Check if there is already an open position with the same MagicNumber
         if (PositionSelect(Symbol()))
           {
            if (PositionGetInteger(POSITION_MAGIC) == MagicNumber)
              {
               return; // Exit OnTick if there's an open position with the same MagicNumber
              }
           }
      
         //--- Calculate the indicator
         if (CopyBuffer(indicator_handle, 5, 0, 2, Buffer6) <= 0 || CopyBuffer(indicator_handle, 6, 0, 2, Buffer7) <= 0)
           {
            Print("Failed to copy buffer values. Error: ", GetLastError());
            return;
           }
      
         //--- Check for a buy signal
         if (Buffer7[0] != EMPTY_VALUE)
           {
            double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
            double sl = NormalizeDouble(ask - StopLoss * _Point, _Digits);
            double tp = NormalizeDouble(ask + TakeProfit * _Point, _Digits);
      
            //--- Prepare the buy order request
            MqlTradeRequest request;
            MqlTradeResult result;
            ZeroMemory(request);
            ZeroMemory(result);
      
            request.action = TRADE_ACTION_DEAL;
            request.symbol = Symbol();
            request.volume = Lots;
            request.type = ORDER_TYPE_BUY;
            request.price = ask;
            request.sl = sl;
            request.tp = tp;
            request.deviation = Slippage;
            request.magic = MagicNumber;
            request.comment = "Buy Order";
      
            //--- Send the buy order
            if (!OrderSend(request, result))
              {
               Print("Error opening buy order: ", result.retcode);
              }
            else
              {
               Print("Buy order opened successfully! Ticket: ", result.order);
              }
           }
      
         //--- Check for a sell signal
         if (Buffer6[0] != EMPTY_VALUE)
           {
            double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
            double sl = NormalizeDouble(bid + StopLoss * _Point, _Digits);
            double tp = NormalizeDouble(bid - TakeProfit * _Point, _Digits);
      
            //--- Prepare the sell order request
            MqlTradeRequest request;
            MqlTradeResult result;
            ZeroMemory(request);
            ZeroMemory(result);
      
            request.action = TRADE_ACTION_DEAL;
            request.symbol = Symbol();
            request.volume = Lots;
            request.type = ORDER_TYPE_SELL;
            request.price = bid;
            request.sl = sl;
            request.tp = tp;
            request.deviation = Slippage;
            request.magic = MagicNumber;
            request.comment = "Sell Order";
      
            //--- Send the sell order
            if (!OrderSend(request, result))
              {
               Print("Error opening sell order: ", result.retcode);
              }
            else
              {
               Print("Sell order opened successfully! Ticket: ", result.order);
              }
           }
        }
      
      //+------------------------------------------------------------------+
      //| Trade function                                                   |
      //+------------------------------------------------------------------+
      void OnTrade()
        {
         //--- Handle trade events if necessary
        }
      
      //+------------------------------------------------------------------+
      //| Tester function                                                  |
      //+------------------------------------------------------------------+
      double OnTester()
        {
         double ret = 0.0;
         //--- Custom calculations for strategy tester
         return (ret);
        }
      
      //+------------------------------------------------------------------+
      //| TesterInit function                                              |
      //+------------------------------------------------------------------+
      void OnTesterInit()
        {
         //--- Initialization for the strategy tester
        }
      
      //+------------------------------------------------------------------+
      //| TesterPass function                                              |
      //+------------------------------------------------------------------+
      void OnTesterPass()
        {
         //--- Code executed after each pass in optimization
        }
      
      //+------------------------------------------------------------------+
      //| TesterDeinit function                                            |
      //+------------------------------------------------------------------+
      void OnTesterDeinit()
        {
         //--- Cleanup after tester runs
        }
      
      //+------------------------------------------------------------------+
      //| ChartEvent function                                              |
      //+------------------------------------------------------------------+
      void OnChartEvent(const int id,
                        const long &lparam,
                        const double &dparam,
                        const string &sparam)
        {
         //--- Handle chart events here
        }
      //+------------------------------------------------------------------+
      

      コンパイルに成功した次のセグメントでは、プログラムをテストします。


      テスト

      MetaEditorでは、[Compile]または[Run]ボタンを使って、テスト用のプログラムを準備することができます。この場合、コンパイルは成功し、ストラテジーテスターを使ってBoom 500 Indexのテストを開始しました。

      ストラテジーテスターの起動

      ナビゲーターからのテスターの起動

      ストラテジーテスターパネルが開き、右下の[スタート]ボタンをクリックする前にいくつかの設定を調整できます。例えば、EAのデフォルトのロットサイズは0.1ロットに設定されていますが、Boom500指数の場合、今回は最低0.2ロットまで増やす必要がありました。

      ストラテジーテスター

      ストラテジーテスターパネル

      驚くべきことに、私たちのシステムはストラテジーテスターで優れた成績を収めました。

      メタテスターの再生

      ストラテジーテスターの可視化:Trend Constraint EA


      結論

      リスクとリターンの矩形を追加したことで、トレーダーは取引を明確かつグラフィカルに表示できるようになり、ポジションの監視と管理が容易になりました。この視覚的支援は、迅速な意思決定が求められる動きの速い市場において特に有用です。矩形は、取引の潜在的な結果を常に意識させる役割を果たし、トレーダーが当初の取引計画を維持するのに役立ちます。

      Trend Constraint V1.09指標とEAのコラボレーションの成功は、取引戦略におけるツール間の相乗効果の重要性を浮き彫りにしています。指標は潜在的なトレンドや反転を識別し、EAはこの情報に基づいて取引を実行し、リスクを管理します。この統合されたアプローチは、より一貫性のある効果的な取引戦略につながります。

      以下に、使用した指標、スクリプト、EAを添付します。まだ改善や修正の余地がありますが、この情報が貴重であることを願っています。コメント欄でご意見をお聞かせください。取引をお楽しみください。

      添付ファイル 詳細
      Trend_Constraint V1.09.mq5 EAで動作する指標のソースコード
      Trend Constraint R-R.mq5 リスクとリターンの矩形スクリプトのソースコード
      Trend Constraint Expert.mq5 Trend Constraint V1.09に厳密に対応するEAのソースコード

      目次へ移動

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

      ニュース取引が簡単に(第3回):取引の実施 ニュース取引が簡単に(第3回):取引の実施
      この記事では、ニュース取引エキスパートアドバイザー(EA)で、データベースに保存されている経済指標カレンダーに基づいて取引を開始します。さらに、EAのグラフィックを改善し、今後の経済指標カレンダーイベントに関するより適切な情報を表示する予定です。
      MQL5の統合:Python MQL5の統合:Python
      Pythonは、特に金融、データサイエンス、人工知能、機械学習の分野で多くの特徴を持つ、よく知られた人気のプログラミング言語です。また、Pythonは取引にも有効な強力なツールです。MQL5では、この強力な言語を統合して使用することで、目的を効果的に達成することができます。本記事では、Pythonの基本的な情報を学んだ後、MQL5でPythonを統合して使用する方法を紹介します。
      古典的な戦略を再構築する(第4回):SP500と米財務省中期証券 古典的な戦略を再構築する(第4回):SP500と米財務省中期証券
      この連載では、最新のアルゴリズムを用いて古典的な取引戦略を分析し、AIによって戦略を改善できるかどうかを検証します。本日の記事では、SP500と米財務省中期証券との関係を活用した古典的な取引手法を再考します。
      コードロックアルゴリズム(CLA) コードロックアルゴリズム(CLA)
      この記事では、コードロックを単なるセキュリティメカニズムとしてではなく、複雑な最適化問題を解くためのツールとして再考し、新たな視点から捉えます。セキュリティ装置にとどまらず、最適化への革新的アプローチのインスピレーション源となるコードロックの世界をご紹介します。各ロックが特定の問題の解を表す「ロック」の母集団を作り、機械学習や取引システム開発など様々な分野でこれらのロックを「ピッキング」し、最適解を見つけるアルゴリズムを構築します。