English
preview
古典的な戦略を再構築する(第13回):移動平均線のクロスオーバーにおける遅延の最小化

古典的な戦略を再構築する(第13回):移動平均線のクロスオーバーにおける遅延の最小化

MetaTrader 5 | 22 4月 2025, 10:14
15 0
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana

これまでのディスカッションでは、移動平均クロスオーバー戦略の効率性を最大化するために、さまざまな視点からアプローチを検討してきました。簡単にまとめると、200以上の銘柄を対象に実験をおこなった結果、将来の価格そのものを予測するよりも、将来の移動平均値を予測する方が、コンピュータはより高い精度で学習する傾向があることがわかりました。その分析を紹介した記事へのリンクはこちらです。またこの考えをさらに発展させ、2本の移動平均をモデル化し、クロスオーバーの発生を他の市場参加者よりも早く予測し、トレードに反映させるという戦略にも取り組みました。2番目の記事へのリンクもこちらに記載したので、すぐにご覧いただけます。 

そして本日のディスカッションでは、元の戦略をさらに拡張し、トレーディング戦略に内在する遅延を最小限に抑える手法を提案します。従来のクロスオーバー戦略では、2つの移動平均の間に期間の差が必要とされてきました。しかし、私たちはこの伝統的な考え方をあえて取り払い、両方の移動平均に同じ期間を設定するというアプローチを取ります。

この時点で、両方の移動平均が同じ期間なら、どのようにしてクロスオーバーが発生するのかと疑問に思われる方もいるでしょう。その答えは、実は非常にシンプルです。1つの移動平均を「始値」に、もう1つを「終値」に適用することで、同一期間であってもクロスオーバーが生まれます。 
始値に基づく移動平均が終値に基づく移動平均より上にあるとき、それは価格が始値から終値にかけて下落していることを意味します。つまり、価格が下降しているというサインになります。反対に、終値の移動平均が始値の移動平均を上回っているときには、価格が上昇している状態だと判断できます。 
移動平均に同一の期間を使うというのは従来の手法からすると異例ではありますが、本稿では、移動平均クロスオーバー戦略に典型的に見られる「遅れ」に対する懸念を払拭するための一例として、あえてこの方法を選んでいます。

技術的な部分に入る前に、この戦略はあくまで一例であり、より広範なクラスの取引戦略として捉えることができます。つまり、他のテクニカル指標にも容易に応用可能です。たとえば、RSI(相対力指数)を始値、終値、高値、安値にそれぞれ適用することで、下落相場においては、始値に基づいたRSIの方が終値に基づいたRSIよりも高くなるといった、同様の現象が観察されることがあります。


バックテストの概要

本日のディスカッションの意義を正しく理解するために、まずは従来のクロスオーバー戦略によって得られるベンチマークパフォーマンスを確立します。そのうえで、従来の戦略と、今回提案する再構築された戦略のパフォーマンスを比較し、その差異がどれほど意味のあるものかを検証します。

今回の検証対象として選んだのは、EUR/USDペアです。EUR/USDは、世界で最も取引量の多い通貨ペアであり、ほとんどの通貨ペアと比較して非常に高いボラティリティを持ちます。そのため、単純な移動平均クロスオーバー戦略にはあまり向かないと一般的に考えられています。前述のとおり、本検証では日足チャートを使用します。バックテストの期間は、2020年1月1日から2024年12月24日までの約4年間にわたり、この期間中の価格データをもとに検証をおこないます。バックテスト期間は図1にてハイライト表示しています。

図1:MetaTrader 5端末で月足を使用して4年間のEURUSDバックテスト期間を表示する

従来のクロスオーバー戦略は、直感的に理解しやすく、合理的な基本原則に基づいているため、多くのトレーダーに支持されています。しかし、効果的に活用するためには頻繁な最適化が必要となる場合が多く、その適用には注意が必要です。特に、「高速線」と「低速線」にどの期間を設定すべきかは一概に言えず、相場の状況によって大きく変化する可能性があります。 

要約すると、元の戦略は、同一銘柄の終値に基づく、期間の異なる2本の移動平均線が交差するポイントを売買シグナルとして利用するものです。短期の移動平均線が長期の移動平均線の上にある場合、価格は上昇トレンドにあると見なし、今後もその傾向が続くと予測されます。反対に、長期の移動平均線が短期線よりも上にある場合、これは下落トレンドの兆候であり、弱気シグナルと解釈します。例は下の図2に示されています。

図2:従来の移動平均線クロスオーバー戦略の実例。黄色の線は高速移動平均線、白線は低速移動平均線

上の図2では、従来のクロスオーバー戦略の限界を示す一例として、ランダムに選ばれた期間を表示しています。チャート内の垂直線の左側に注目すると、価格の動きが約2か月間にわたってレンジ内にとどまり、方向感のない相場が続いていたことがわかります。このような緩慢な値動きでは、売買シグナルが頻繁に発生してはすぐに反転するため、実際には利益を出しにくい状況となります。しかし、その低調なパフォーマンスの期間を抜けた後、垂直線の右側では価格が本格的なトレンドを形成し、明確な方向性を持って動き出しているのが確認できます。従来のクロスオーバー戦略は、このようなトレンド相場においてこそ真価を発揮します。一方で、本記事で提案している戦略は、こうした課題に対してより洗練されたアプローチを提供しています。



ベンチマークの確立

私たちの取引アプリケーションは、次の4つの主要なコンポーネントから構成されており、それぞれが連携することで、効果的な取引を実現します。

特徴 詳細
システム定数 アプリケーションのトレードロジックに加えた変更によってもたらされる改善点を明確に識別するために使用される
グローバル変数 インジケーターの値、現在の市場価格、その他必要な情報の管理を担当する
イベントハンドラ 適切なタイミングでさまざまな処理を実行し、移動平均クロスオーバー戦略を効果的に運用するための基盤となる
カスタム関数 各関数には特定のタスクが割り当てられており、全体としてシステムの目標達成を支援するように設計させている

ベンチマークバージョンの実装は最小限になります。最初に取り組むべき作業は、両方のテストにおいて固定されたままとなる「システム定数」を設定することです。これらのシステム定数は、異なる取引戦略を公平に比較するために非常に重要であり、本来変更すべきでない設定(たとえば、ストップロスのサイズなど)をテストごとにうっかり変更してしまうのを防ぐ役割を果たします。

//+------------------------------------------------------------------+
//|                                               Channel And MA.mq5 |
//|                                        Gamuchirai Zororo Ndawana |
//|                          https://www.mql5.com/ja/gamuchiraindawa |
//+------------------------------------------------------------------+
#property copyright "Gamuchirai Zororo Ndawana"
#property link      "https://www.mql5.com/ja/gamuchiraindawa"
#property version   "1.00"

//+------------------------------------------------------------------+
//| System constants                                                 |
//+------------------------------------------------------------------+
#define TF_1           PERIOD_D1 //--- Our main time frame
#define TF_2           PERIOD_H1 //--- Our lower time frame
#define ATR_PERIOD     14        //--- The period for our ATR
#define ATR_MULTIPLE   3         //--- How wide should our stops be?
#define VOL            0.01      //--- Trading volume

また、インジケーターの値を取得して現在の市場価格を取得するために使用するいくつかの重要なグローバル変数も定義します。

//+------------------------------------------------------------------+
//| Global variables                                                 |
//+------------------------------------------------------------------+
int    trade;
int    ma_f_handler,ma_s_handler,atr_handler;
double ma_f[],ma_s[],atr[];
double bid,ask;
double o,h,l,c;
double original_sl;

ポジションの管理には取引ライブラリを使用します。 

//+------------------------------------------------------------------+
//| Libraries                                                        |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
CTrade Trade;

MQL5では、エキスパートアドバイザー(EA)はイベントハンドラから構築されます。MetaTrader 5端末ではさまざまな種類のイベントが発生します。これらのイベントは、ユーザーが実行したアクションや新しい価格が提示された場合にトリガーされる可能性があります。各イベントは、イベントがトリガーされるたびに呼び出されるイベントハンドラとペアになっています。したがって、各イベントハンドラが独自の指定された関数を持ち、移動平均クロスオーバー戦略に必要なタスクを実行するために順番に呼び出されるようにアプリケーションを設計しました。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Setup
   setup();
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Release our indicators
   release();
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Update system variables
   update();
  }
//+------------------------------------------------------------------+

システムの起動時に設定関数が呼び出されます。OnInitハンドラは、取引アプリケーションが最初にチャートに適用されたときに呼び出され、テクニカルインジケーターを適用するカスタマイズされたセットアップ関数にコマンドチェーンを渡します。

//+------------------------------------------------------------------+
//| Custom functions                                                 |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Setup our system                                                 |
//+------------------------------------------------------------------+
void setup(void)
  {
   atr_handler  = iATR(Symbol(),TF_1,ATR_PERIOD);

   ma_f_handler = iMA(Symbol(),TF_1,10,0,MODE_EMA,PRICE_CLOSE);
   ma_s_handler = iMA(Symbol(),TF_1,60,0,MODE_EMA,PRICE_CLOSE);
  }

アプリケーションを使用しなくなった場合は、onDeinitイベントハンドラが呼び出され、リリース関数が呼び出され、テクニカルインジケーターによって以前に消費されていたシステムリソースが解放されます。

//+------------------------------------------------------------------+
//| Release variables we do not need                                 |
//+------------------------------------------------------------------+
void release(void)
  {
   IndicatorRelease(atr_handler);
   IndicatorRelease(ma_f_handler);
   IndicatorRelease(ma_s_handler);
  }

新しい市場価格が提示されるたびに、OnTickハンドラが呼び出され、次に更新関数が呼び出されて、利用可能な新しい市場情報が保存されます。その後、ポジションがない場合は、取引のセットアップを探します。それ以外の場合は、空いているポジションを管理します。

/+------------------------------------------------------------------+
//| Update system variables                                          |
//+------------------------------------------------------------------+
void update(void)
  {
//--- Update the system
   static datetime time_stamp;
   datetime current_time = iTime(Symbol(),TF_2,0);
   if(current_time != time_stamp)
     {
      time_stamp = current_time;

      CopyBuffer(atr_handler,0,0,1,atr);
      CopyBuffer(ma_s_handler,0,0,1,ma_s);
      CopyBuffer(ma_f_handler,0,0,1,ma_f);

      o  = iOpen(Symbol(),TF_1,0);
      h  = iHigh(Symbol(),TF_1,0);
      l  = iLow(Symbol(),TF_1,0);
      c  = iClose(Symbol(),TF_1,0);
      bid = SymbolInfoDouble(Symbol(),SYMBOL_BID);
      ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK);

      if(PositionsTotal() == 0)
         find_position();
      if(PositionsTotal() > 0)
         manage_position();
     }
  }

ポジションに入るためのルールは簡単で、すでに上記で詳しく説明しました。高速移動平均が低速移動平均を上回っている場合はロングポジションを取り、その逆の場合はショートポジションを取ります。

//+------------------------------------------------------------------+
//| Find a position                                                  |
//+------------------------------------------------------------------+
void find_position(void)
  {

   if((ma_s[0] > ma_f[0]))
     {
      Trade.Sell(VOL,Symbol(),bid,0,0,"");
      trade = -1;
     }

   if((ma_s[0] < ma_f[0]))
     {
      Trade.Buy(VOL,Symbol(),ask,0,0,"");
      trade = 1;
     }
  }

最後に、ストップロスはAverage True Range (ATR)インジケーターを使用して動的に調整されます。エントリー価格の上下にATR値の固定倍数を追加して、ストップロスとテイクプロフィットレベルをマークします。さらに、過去90日間(1ビジネスサイクル)の平均ATR値も追加します。これをおこなう目的は、市場の最近のボラティリティレベルを考慮することです。最後に、三項演算子を使用してテイクプロフィットとストップロスのレベルを調整します。私たちのルールは、新しいポジションが古いポジションよりも収益性が高い場合にのみストップを更新するというものです。三項演算子を使用すると、このロジックをコンパクトに表現できます。さらに、三項演算子を使用すると、テイクプロフィットとストップロスを互いに独立して簡単に調整できる柔軟性も得られます。

//+------------------------------------------------------------------+
//| Manage our positions                                             |
//+------------------------------------------------------------------+
void manage_position(void)
  {
//--- Select the position
   if(PositionSelect(Symbol()))
     {
      //--- Get ready to update the SL/TP
      double initial_sl  = PositionGetDouble(POSITION_SL);
      double initial_tp  = PositionGetDouble(POSITION_TP);
      //--- Calculate the average ATR move
      vector atr_mean;
      atr_mean.CopyIndicatorBuffer(atr_handler,0,0,90);
      double buy_sl      = (ask - ((ATR_MULTIPLE * atr[0]) + atr_mean.Mean()));
      double sell_sl     = (bid + ((ATR_MULTIPLE * atr[0]) + atr_mean.Mean()));
      double buy_tp      = (ask + ((ATR_MULTIPLE * 0.5 * atr[0]) + atr_mean.Mean()));
      double sell_tp     = (bid - ((ATR_MULTIPLE * 0.5 * atr[0]) + atr_mean.Mean()));
      double new_sl      = ((trade == 1) && (initial_sl <  buy_sl)) ? (buy_sl) : ((trade == -1) && (initial_sl > sell_sl)) ? (sell_sl) : (initial_sl);
      double new_tp      = ((trade == 1) && (initial_tp <  buy_tp)) ? (buy_tp) : ((trade == -1) && (initial_tp > sell_tp)) ? (sell_tp) : (initial_tp);

      if(initial_sl == 0 && initial_tp == 0)
        {
         if(trade == 1)
           {
            original_sl = buy_sl;
            Trade.PositionModify(Symbol(),buy_sl,buy_tp);
           }

         if(trade == -1)
           {
            original_sl = sell_sl;
            Trade.PositionModify(Symbol(),sell_sl,sell_tp);
           }

        }
      //--- Update the position
      else
         if((initial_sl * initial_tp) != 0)
           {
            Trade.PositionModify(Symbol(),new_sl,new_tp);
           }
     }
  }
//+------------------------------------------------------------------+

すべてをまとめると、現在のコードは次のようになります。

//+------------------------------------------------------------------+
//|                                               Channel And MA.mq5 |
//|                                        Gamuchirai Zororo Ndawana |
//|                          https://www.mql5.com/ja/gamuchiraindawa |
//+------------------------------------------------------------------+
#property copyright "Gamuchirai Zororo Ndawana"
#property link      "https://www.mql5.com/ja/gamuchiraindawa"
#property version   "1.00"

//+------------------------------------------------------------------+
//| This version off the application is mean reverting               |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| System constants                                                 |
//+------------------------------------------------------------------+
#define TF_1           PERIOD_D1 //--- Our main time frame
#define TF_2           PERIOD_H1 //--- Our lower time frame
#define ATR_PERIOD     14        //--- The period for our ATR
#define ATR_MULTIPLE   3         //--- How wide should our stops be?
#define VOL            0.01         //--- Trading volume

//+------------------------------------------------------------------+
//| Global variables                                                 |
//+------------------------------------------------------------------+
int    trade;
int    ma_f_handler,ma_s_handler,atr_handler;
double ma_f[],ma_s[],atr[];
double bid,ask;
double o,h,l,c;
double original_sl;

//+------------------------------------------------------------------+
//| Libraries                                                        |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
CTrade Trade;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Setup
   setup();
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Release our indicators
   release();
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Update system variables
   update();
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Custom functions                                                 |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Setup our system                                                 |
//+------------------------------------------------------------------+
void setup(void)
  {
   atr_handler  = iATR(Symbol(),TF_1,ATR_PERIOD);

   ma_f_handler = iMA(Symbol(),TF_1,10,0,MODE_EMA,PRICE_CLOSE);
   ma_s_handler = iMA(Symbol(),TF_1,60,0,MODE_EMA,PRICE_CLOSE);
  }

//+------------------------------------------------------------------+
//| Release variables we do not need                                 |
//+------------------------------------------------------------------+
void release(void)
  {
   IndicatorRelease(atr_handler);
   IndicatorRelease(ma_f_handler);
   IndicatorRelease(ma_s_handler);
  }

//+------------------------------------------------------------------+
//| Update system variables                                          |
//+------------------------------------------------------------------+
void update(void)
  {
//--- Update the system
   static datetime time_stamp;
   datetime current_time = iTime(Symbol(),TF_2,0);
   if(current_time != time_stamp)
     {
      time_stamp = current_time;

      CopyBuffer(atr_handler,0,0,1,atr);
      CopyBuffer(ma_s_handler,0,0,1,ma_s);
      CopyBuffer(ma_f_handler,0,0,1,ma_f);

      o  = iOpen(Symbol(),TF_1,0);
      h  = iHigh(Symbol(),TF_1,0);
      l  = iLow(Symbol(),TF_1,0);
      c  = iClose(Symbol(),TF_1,0);
      bid = SymbolInfoDouble(Symbol(),SYMBOL_BID);
      ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK);

      if(PositionsTotal() == 0)
         find_position();
      if(PositionsTotal() > 0)
         manage_position();
     }
  }

//+------------------------------------------------------------------+
//| Find a position                                                  |
//+------------------------------------------------------------------+
void find_position(void)
  {

   if((ma_s[0] > ma_f[0]))
     {
      Trade.Sell(VOL,Symbol(),bid,0,0,"");
      trade = -1;
     }

   if((ma_s[0] < ma_f[0]))
     {
      Trade.Buy(VOL,Symbol(),ask,0,0,"");
      trade = 1;
     }
  }

//+------------------------------------------------------------------+
//| Manage our positions                                             |
//+------------------------------------------------------------------+
void manage_position(void)
  {
//--- Select the position
   if(PositionSelect(Symbol()))
     {
      //--- Get ready to update the SL/TP
      double initial_sl  = PositionGetDouble(POSITION_SL);
      double initial_tp  = PositionGetDouble(POSITION_TP);
      //--- Calculate the average ATR move
      vector atr_mean;
      atr_mean.CopyIndicatorBuffer(atr_handler,0,0,90);
      double buy_sl      = (ask - ((ATR_MULTIPLE * atr[0]) + atr_mean.Mean()));
      double sell_sl     = (bid + ((ATR_MULTIPLE * atr[0]) + atr_mean.Mean()));
      double buy_tp      = (ask + ((ATR_MULTIPLE * 0.5 * atr[0]) + atr_mean.Mean()));
      double sell_tp     = (bid - ((ATR_MULTIPLE * 0.5 * atr[0]) + atr_mean.Mean()));
      double new_sl      = ((trade == 1) && (initial_sl <  buy_sl)) ? (buy_sl) : ((trade == -1) && (initial_sl > sell_sl)) ? (sell_sl) : (initial_sl);
      double new_tp      = ((trade == 1) && (initial_tp <  buy_tp)) ? (buy_tp) : ((trade == -1) && (initial_tp > sell_tp)) ? (sell_tp) : (initial_tp);

      if(initial_sl == 0 && initial_tp == 0)
        {
         if(trade == 1)
           {
            original_sl = buy_sl;
            Trade.PositionModify(Symbol(),buy_sl,buy_tp);
           }

         if(trade == -1)
           {
            original_sl = sell_sl;
            Trade.PositionModify(Symbol(),sell_sl,sell_tp);
           }

        }
      //--- Update the position
      else
         if((initial_sl * initial_tp) != 0)
           {
            Trade.PositionModify(Symbol(),new_sl,new_tp);
           }
     }
  }
//+------------------------------------------------------------------+

現在の戦略ではAIや曲線あてはめ技法を採用していないため、利用可能なデータへの過剰適合を心配することなく、簡単なバックテストを実行できます。さらに、調整する必要がある入力パラメータはありません。したがって、現時点ではMetaTrader 5端末のフォワードテスト機能を使用する必要がないため、[Forward]を[No]に設定しました。

図3:バックテストの日付の選択

さらに、取引アプリケーションを可能な限り厳しい環境下でテストすることは、良い実践とされています。そのため、本日のバックテストでは「ランダム遅延」モードを選択し、モデリングには実ティックを使用する設定としています。実ティックを使用する場合、過去データのダウンロードに他のモード(例:「始値のみ」)よりも時間がかかる可能性がありますが、実ティックに基づくライブ環境での結果により近いパフォーマンスを得ることができます。

2番目の設定

図4:EURUSDクロスオーバーバックテストの2番目の設定

単純なクロスオーバー戦略を用いて得られた結果を分析すると、この戦略を元の形のまま運用した場合に直面する可能性のある問題点がすぐに見えてきます。バックテストの開始から2022年7月頃までの期間を見てみると、戦略は収支トントンの状態を維持するのに精一杯だったことがわかります。これは、バックテスト期間のほぼ半分、つまり約2年間にわたるドローダウン期間を意味します。このような長期にわたるドローダウンは望ましいものではなく、自動で資金を運用する戦略として信頼できる性質のものではありません。

図5:MAクロスオーバー戦略に従って生成された損益曲線を分析する

元の形での戦略はかろうじて利益が出ている程度であり、全体の約61%の取引で損失を出していることがわかります。この結果から、戦略に対する期待値は非常に低く、さらにシャープレシオがほぼゼロに近いという事実が、私たちの悲観的な見方を裏付けています。しかし注目すべきは、取引ロジックにいくつかのシンプルな調整を加えるだけで、戦略のパフォーマンスを大幅に改善できるという点です。 

図6:従来のMAクロスオーバー戦略を使用したパフォーマンスの詳細な概要



元の戦略を改良する

下の図7では、新たに提案するクロスオーバー戦略を視覚的に示しています。青と緑の線は、それぞれ終値(青)と始値(緑)を基にした5期間の移動平均です。青の移動平均が緑の移動平均の上にあるとき、価格が上昇傾向にあることがわかります。また、クロスオーバーが価格の変化に対してどれだけ迅速に反応しているかにも注目してください。従来のクロスオーバー戦略では、市場トレンドの変化を反映するまでに時間差が生じることが一般的ですが、今回の新しい戦略では、トレンドの変化をすばやく察知できるだけでなく、移動平均同士が頻繁に交差しているものの、実際には価格がほとんど動いていない統合相場の期間も明確に捉えることができます。

図7:EURUSD日足チャートで新しいクロスオーバー戦略を視覚化する

これまでのところ、私たちのシステムは利益を上げて取引することができていますが、さらに改善できる可能性があります。私たちがシステムに加える唯一の変更は、ポジションがトリガーされる条件を変更することです。

変化 詳細
取引
ルール
私たちの伝統的なルールでは、高速移動平均が低速移動平均を上回っているときに購入します。代わりに、始値移動平均が終値移動平均を上回っているときに購入します。

望ましい改善を実現するには、現在の取引戦略の元の形にいくつかの変更を加える必要があります。

変化 詳細
追加のシステム変数 始値と終値の移動平均の期間を固定する新しいシステム変数が必要になります。
新しいグローバル変数 注目している新しい情報を追跡するために、新しいグローバル変数が作成されます。
カスタム関数の変更 これまでに構築したカスタマイズされた機能の一部は、現在採用している新しいシステム設計を考慮して拡張する必要があります。

システムのその他の部分はほとんどすべて保存されます。移動平均線のクロスオーバーに対する見方を変えることでもたらされる改善点を特定したいと考えています。したがって、目標を実現するために、まず移動平均の期間を固定する新しいシステム定数を作成します。

//--- Omitted code that has not changed
#define MA_PERIOD      2         //--- The period for our moving average following the close

次に、追跡している新しい情報用に新しいグローバル変数を定義する必要があります。4つの価格(始値、高値、安値、終値)ごとに移動平均ハンドラを作成します。

//--- Omitted code that have not changed
int    ma_h_handler,ma_l_handler,ma_c_handler,ma_o_handler;
double ma_h[],ma_l[],ma_c[],ma_o[];

取引アプリケーションを起動するときに、すでに使い慣れているインジケーターに加えて、いくつかの追加インジケーターをロードする必要があります。

//+------------------------------------------------------------------+
//| Setup our system                                                 |
//+------------------------------------------------------------------+
void setup(void)
  {
   atr_handler  = iATR(Symbol(),TF_1,ATR_PERIOD);
   ma_h_handler = iMA(Symbol(),TF_1,MA_PERIOD,0,MODE_EMA,PRICE_HIGH);
   ma_l_handler = iMA(Symbol(),TF_1,MA_PERIOD,0,MODE_EMA,PRICE_LOW);
   ma_c_handler = iMA(Symbol(),TF_1,MA_PERIOD,0,MODE_EMA,PRICE_CLOSE);
   ma_o_handler = iMA(Symbol(),TF_1,MA_PERIOD,0,MODE_EMA,PRICE_OPEN);
  }

同じことが、EAの削除を担当するカスタマイズされた機能にも当てはまり、システムに導入した新しいインジケーターに対応するために拡張されます。

//+------------------------------------------------------------------+
//| Release variables we do not need                                 |
//+------------------------------------------------------------------+
void release(void)
  {
   IndicatorRelease(atr_handler);
   IndicatorRelease(ma_h_handler);
   IndicatorRelease(ma_l_handler);
   IndicatorRelease(ma_c_handler);
   IndicatorRelease(ma_o_handler);
  }

最後に、取引を開始するために使用する条件を、新しい取引ロジックに沿うように更新する必要があります。

//+------------------------------------------------------------------+
//| Find a position                                                  |
//+------------------------------------------------------------------+
void find_position(void)
  {

   if((ma_o[0] > ma_c[0]))
     {
      Trade.Sell(VOL,Symbol(),bid,0,0,"");
      trade = -1;
     }

   if((ma_o[0] < ma_c[0]))
     {
      Trade.Buy(VOL,Symbol(),ask,0,0,"");
      trade = 1;
     }
  }

これが収益にどのような影響を与えるかを見てみましょう。まず、最初のテストで使用したのと同じ期間に取引を行うように新しい取引アプリケーションを設定します。

図8:最初のテストで使用したのと同じ期間に取引するために、新しく改良された取引アルゴリズムを設定する

さらに、テストが偏りのないことを確認するために、両方のバックテストを同一の条件で実行する必要があります。そうでない場合、1つの戦略に不当な優位性が与えられると、これまでのテストの整合性が疑われることになります。

図9:公平な比較をおこなうために、両方のテストでテスト条件が同一であることを確認する

当初得られた結果と比べて、すでに大きな改善が見られます。最初のバックテストでは、最初の2年間を損益分岐点の状態で過ごすことに費やしました。しかし、新しい取引戦略ではこの制約を打破し、4年間を通して一貫して利益を上げることができました。 

図10:新しい取引戦略は、従来のクロスオーバー戦略では解決が困難だった不採算取引期間を打破した

最初のバックテストでは、システムは合計41回の取引をおこない、最新のバックテストでは合計42回の取引をおこないました。つまり、新しいシステムは従来の取引方法よりも多くのリスクを取っていることになります。時間が経過すれば、この差はさらに広がる可能性があるからです。しかし、新しいシステムは古いシステムよりも多くの取引をおこなっているように見えますが、古いシステムの合計利益は59.57ドルで、現在ではその利益が2倍以上となり、125.36ドルに達しています。現時点では、私たちの取引システムは最小ロットで1回の取引をおこなうように制限していることにご留意ください。さらに、最初のシステムでは総損失が410.02ドルでしたが、新しい戦略ではその総損失が330.74ドルに減少しています。 

システム設計の際は、トレードオフを考慮する必要があります。新しいシステムは明らかにより良いパフォーマンスを発揮していますが、平均利益が29.35ドルから22.81ドルに減少したことにも留意する必要があります。これは、新しいシステムでは古いシステムが利益を上げていた取引を見逃すことが稀にあるためです。しかし、パフォーマンスの向上を考慮すると、この偶発的な後悔は十分に合理的と言えるかもしれません。 

さらに、シャープレシオは最初のテストで0.18から、現在のテストでは0.5に増加しました。これは、私たちが資本をより効率的に活用できていることを示す良い兆しです。加えて、負け取引の割合は最初のテストで60.98%だったのに対し、現在では52.38%という新たな低水準に達しています。

図11:新しい取引戦略のパフォーマンスの詳細なレビュー



結論

私たちのコミュニティのほとんどのメンバーは、独立した開発者として、プロジェクトに一人で取り組んでいます。そのような立場にある皆さんにとって、ここで提案したようなシンプルなアルゴリズムは、より実用的な解決策となるでしょう。保守、開発、拡張が容易です。複雑で大規模なコードベースを一人で管理するのは、経験豊富な開発者にとっても簡単なことではありません。また、アルゴリズム取引コミュニティに初めて参加する方々には、この戦略が特に役立つかもしれません。この記事を楽しんでいただけたなら、ぜひ次回のディスカッションにもご参加ください。次回は、今日私たちが達成した最高の結果を超える成果を目指します。 

ファイル名 詳細
Open & Close MA Cross 移動平均クロスオーバー戦略の新しい再考バージョン
Traditional MA Cross 移動平均クロスオーバーの古典的な実装

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

添付されたファイル |
ログレコードをマスターする(第2回):ログのフォーマット処理 ログレコードをマスターする(第2回):ログのフォーマット処理
この記事では、ライブラリ内でログフォーマッターを作成し、適用する方法について詳しく解説します。フォーマッターの基本構造から実践的な実装例まで幅広く取り上げます。この記事を読み終える頃には、ライブラリ内でログを整形するために必要な知識を習得し、その裏側で何がどのように動作しているのかを理解できるようになります。
MQL5での取引戦略の自動化(第3回):ダイナミック取引管理のためのZone Recovery RSIシステム MQL5での取引戦略の自動化(第3回):ダイナミック取引管理のためのZone Recovery RSIシステム
この記事では、MQL5を使ってZone Recovery RSI EAシステムを構築し、RSIシグナルによって取引を開始し、損失を管理するためのリカバリーストラテジーを実装します。取引エントリー、リカバリーロジック、ポジション管理を自動化するために、ZoneRecoveryクラスを作成します。この記事の最後では、EAのパフォーマンスを最適化し、その有効性を高めるためのバックテストの洞察を紹介します。
Candlestick Trend Constraintモデルの構築(第10回):戦略的ゴールデンクロスとデスクロス(EA) Candlestick Trend Constraintモデルの構築(第10回):戦略的ゴールデンクロスとデスクロス(EA)
移動平均線のクロスオーバーに基づくゴールデンクロスおよびデッドクロス戦略は、長期的な市場トレンドを見極める上で最も信頼性の高い指標の一つであることをご存知でしょうか。ゴールデンクロスは、短期移動平均線が長期移動平均線を上回るときに強気トレンドの到来を示します。一方、デッドクロスは、短期移動平均線が長期線を下回ることで弱気トレンドの兆候を示します。これらの戦略は非常にシンプルでありながら効果的ですが、手動で運用すると機会の逸失やエントリーの遅れが発生しやすいという課題があります。しかし、MQL5を活用してTrend Constraintエキスパートアドバイザー(EA)内で自動化することで、これらの戦略は独立して機能し、市場の反転に迅速かつ効率的に対応できるようになります。また、制約付きの戦略と組み合わせることで、広範なトレンドと整合性を保つことができます。このアプローチにより、反転戦略とトレンドフォロー戦略のシームレスな統合が実現され、精密なエントリーと一貫したパフォーマンス向上をもたらします。
プライスアクション分析ツールキットの開発(第6回):Mean Reversion Signal Reaper プライスアクション分析ツールキットの開発(第6回):Mean Reversion Signal Reaper
いくつかの概念は一見するとシンプルに思えるかもしれませんが、実際にそれを形にするのは想像以上に難しいことがあります。この記事では、平均回帰(Mean Reversion)戦略を用いて市場を巧みに分析するエキスパートアドバイザー(EA)の自動化に取り組んだ、革新的なアプローチをご紹介します。この魅力的な自動化プロセスの奥深さを、一緒に紐解いていきましょう。