English Deutsch
preview
出来高ベースの取引システムを構築し最適化する方法(チャイキンマネーフロー:CMF)

出来高ベースの取引システムを構築し最適化する方法(チャイキンマネーフロー:CMF)

MetaTrader 5トレーディング | 16 4月 2025, 14:25
781 0
Mohamed Abdelmaaboud
Mohamed Abdelmaaboud

はじめに

新しいインジケーターの作成、その概念に基づく取引システムの構築、そして利益とリスクに関するより良い洞察と結果を得るために、それらのシステムを最適化するという観点から、この新しいインジケーターを検討する記事へようこそ。この記事では、チャイキンマネーフロー(CMF)インジケーターと呼ばれる、新しい出来高ベースのテクニカルインジケーターを紹介します。

ここで重要な点を述べておくのが良いでしょう。このタイプの記事の主な目的は、これらのツールが役立つかどうかを確認するために、テストに加えて、これらのツールの性質に基づいて、単独で、または他のツールと組み合わせて使用できる新しい技術ツールを共有することです。また、これらのツールを最適化して、より良い結果を得ることも重要です。

この新しいインジケーター(CMF)は、以下のトピックに従って作成されます。

  1. チャイキンマネーフロー:このテクニカルツールの定義、計算方法、使用方法を理解し、基本的な知識を身につけます。
  2. カスタムチャイキンマネーフローインジケーター:設定を変更または適用してカスタムインジケーターをコーディングする方法を学びます。
  3. チャイキンマネーフロー戦略:私たちの取引システムの一部であるいくつかのシンプルな取引戦略を見ていきます。
  4. チャイキンマネーフロー取引システム:これらのシンプルな取引システムを構築、テスト、最適化します。
  5. 結論

免責条項:すべての情報は「現状有姿」で提供されており、情報提供のみを目的としており、取引やアドバイスを目的としたものではありません。いかなる結果も保証するものではありません。読者がこれらの資料を自分の取引口座で使用する場合、自己責任でおこなってください。



チャイキンマネーフロー

チャイキンマネーフロー(CMF)は、価格変動を考慮した出来高に基づくテクニカル指標です。後で説明するように、これを単独で使用することも、他のツールと組み合わせて使用して、より優れた洞察を得ることもできます。CMFは、一定期間にわたる金融商品の蓄積と分配を監視するためにマーク・チャイキンによって開発された指標です。CMFの基本的な考え方は、終値が高値に近づくにつれて蓄積が進むというものです。一方、終値が安値に近づくと、分配のサインとなります。チャイキンマネーフローの結果がプラスになるのは、出来高の増加時に、価格の動きが一貫してバーの中間点を上回って終値がついた場合です。チャイキンマネーフローの結果がマイナスになるのは、出来高の増加時に、価格の動きが一貫してバーの中間点を下回って終値がついた場合です。

CMF指標を計算する手順は次のとおりです。

  • マネーフロー乗数の計算
マネーフロー乗数=[(終値-安値)-(高値-終値)]/(高値-安値)
  • マネーフロー量の計算

マネーフロー量=マネーフロー乗数*期間の出来高

  • CMFの計算

n期間CMF=n期間のマネーフロー量の合計/n期間の出来高の合計

インジケーターを計算した後、+1から-1の範囲が読み取られ、次の図と同じであることがわかります。

cmfInd

インジケーターはゼロの周りで振動する線になる可能性があるため、CMFと買いまたは売りの勢いの変化は、0の上下のクロスオーバーによって識別できます。ゼロを超える正の値は購買力を示します。一方、販売力が優勢になり始めると、指標はゼロを下回ります。CMFがゼロラインの周りで振動する場合、買いと売りの力が比較的均等であるか、明確な傾向がないことを示します。CMFは、取引されている商品の傾向を識別および評価するためのツールとして使用されます。

次のセクションでは、線の代わりにヒストグラムとして描画するなど、インジケーターを自由にファインチューニングしたり、取引の改善に役立つと思われるその他の変更を行ったりすることができます。これが、コードを記述または編集して、好みに応じてインジケーターをカスタマイズする主な目的です。


カスタムチャイキンマネーフローインジケーター

このセクションでは、カスタマイズされたCMFインジケーターを段階的にコーディングして、後でシンプルな取引システムで使用できるようにする方法を説明します。後でわかるように、カスタマイズはシンプルです。主な目的は、この出来高インジケーターに応じて使用できるインジケーターと戦略に関する新しい洞察とアイデアを読者に知ってもらうことであるためです。

このカスタマイズされたインジケーターをコーディングするには、次の手順に従います。

#propertyの横に追加パラメータを指定して、インジケーターの動作と外観に関する以下の値を識別します。

  • description定数を使用し、「Chaikin Money Flow」を指定してインジケーターを説明します。
#property description "Chaikin Money Flow"
  • indicator_separate_window定数を使用して、インジケーターをチャート上の別のウィンドウに表示する場所を指定します。
#property indicator_separate_window
  • indicator_buffers定数を使用して、インジケーター計算用のバッファの数を4に設定します。
#property indicator_buffers 4
  • indicator_plots定数を使用して、インジケーター内のグラフィックシリーズの数を1に設定します。
#property indicator_plots   1
  • indicator_width1定数を使用してグラフシリーズの線の太さを設定します(3)。1はグラフシリーズの番号です。
#property indicator_width1  3
  • indicator_level1、indicator_level2、indicator_level3定数を使用して、別ウィンドウのインジケーターにおける0、0.20、-0.20の水平レベル(レベル1、2、3)を指定します。
#property indicator_level1  0
#property indicator_level2  0.20
#property indicator_level3  -0.20
  • indicator_levelstyleを使用し、インジケーターの水平レベルのスタイルをSTYLE_DOTに設定します。
#property indicator_levelstyle STYLE_DOT
  • indicator_levelwidthを使用し、インジケーターの水平レベルの厚さを0に設定します。
#property indicator_levelwidth 0
  • indicator_levelcolorを使用し、インジケーターの水平レベルの色をclrBlackに設定します。
#property indicator_levelcolor clrBlack
  • indicator_type1を使用し、グラフ描画のタイプをDRAW_HISTOGRAMに設定します。
#property indicator_type1   DRAW_HISTOGRAM
  • indicator_color1を使用して、N行目を表示する色をclrBlueに設定します。
#property indicator_color1  clrBlue
  • 「input」を使用して、指標計算で使用される期間と出来高タイプに関してユーザーの好みを設定するための入力を指定します。
input int                 periods=20; // Periods
input ENUM_APPLIED_VOLUME volumeTypeInp=VOLUME_TICK;  // Volume Type
  • cmfBufferの配列を宣言しkます。
double                    cmfBuffer[];

OnInit()関数でインジケーターを設定します。

SetIndexBuffer関数を使用して、double型の1次元動的配列をCMFインジケーターバッファにリンクします。パラメータは次のとおりです。

  • index:バッファのインデックスを指定する(0)
  • buffer[]:cmfBufferとなる配列を指定する 
  • data_type:インジケーター配列に格納されるデータ型を指定する
SetIndexBuffer(0,cmfBuffer,INDICATOR_DATA);

IndicatorSetInteger関数を使用して、インジケーターの対応するプロパティの値を設定します。パラメータは次のとおりです。

  • prop_id:識別子を定義する(INDICATOR_DIGITS)
  • prop_value:設定する小数値を定義する(5)
IndicatorSetInteger(INDICATOR_DIGITS,5);

PlotIndexSetInteger関数を使用して、図面のプロットを開始するタイミングを設定します。パラメータは次の通りです。

  • plot_index:プロットスタイルのインデックスを指定する(0)
  • prop_id:ENUM_PLOT_PROPERTY_INTEGER列挙の1つとして、プロパティ識別子を指定する(PLOT_DRAW_BEGIN)
  • prop_value:プロパティとして設定する値を指定する(0)
PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,0);

IndicatorSetString関数を使用して、インジケーターの名前と期間を指定します。パラメータは次の通りです。

  • prop_id:ENUM_CUSTOMIND_PROPERTY_STRING列挙のいずれかになる識別子を指定する(INDICATOR_SHORTNAME)
  • prop_value:インジケーターのテキスト値を指定する("Chaikin Money Flow("+string(periods)+")")
IndicatorSetString(INDICATOR_SHORTNAME,"Chaikin Money Flow("+string(periods)+")");

OnCalculateセクションでCMF値を計算します。

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])

if文を使用して、CMF値を計算するためのデータがあるかどうかを確認します。

   if(rates_total<periods)
      return(0);

データがあり、以前にCMFを計算した場合、現在の値の計算は開始しません。

   int initPos = prev_calculated -1;
   if(initPos<0) initPos = 0;

CMFを以下の手順で計算します。

  • 出来高の種類を選択します。
  • ループで各バーのCMF値を計算します。
  • sumAccDis、sumVolを宣言します。
  • thisTickVolume変数を宣言および定義した後、宣言されたsumAccDis変数とsumVol変数ををループで計算します。
  • 結果をcmfBufferのバッファに保存します。
  • 計算された値を返します。
   if(volumeTypeInp==VOLUME_TICK)
   {
      for(int pos = initPos;pos<=rates_total-periods;pos++)
      {
         double sumAccDis = 0;
         long sumVol = 0;
         
         for(int i = 0; i < periods && !IsStopped(); ++i)
         {
            long thisTickVolume = tick_volume[pos+i];
            sumVol += thisTickVolume;
            sumAccDis += AccDis(high[pos+i], low[pos+i], close[pos+i], thisTickVolume);
         }
         
         cmfBuffer[pos+periods-1] = sumAccDis/sumVol;
      }
   }
   else
   {
      for(int pos = initPos;pos<=rates_total-periods;pos++)
      {
         double sumAccDis = 0;
         long sumVol = 0;
         
         for(int i = 0; i < periods && !IsStopped(); ++i)
         {
            long thisTickVolume = volume[pos+i];
            sumVol += thisTickVolume;
            sumAccDis += AccDis(high[pos+i], low[pos+i], close[pos+i], thisTickVolume);
         }
         
         cmfBuffer[pos+periods-1] = sumAccDis/sumVol;
      }
   }
   
   return (rates_total-periods-10);

コードで使用されているAccDis関数を宣言して4つの変数(high、low、close、volume)を持たせ、CMFの計算に使用されるバーのMoneyFlowMultiplierとMoneyFlowVolumeを計算します。

double AccDis(double high,double low,double close,long volume)
{
   double res=0;
   
   if(high!=low)
      res=(2*close-high-low)/(high-low)*volume;
   
   return(res);
}

これで、カスタマイズされたCMFインジケーターを作成するためのコードが完成しました。1つのブロック内の完全なコードを以下に示します。

#property description "Chaikin Money Flow"
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_plots   1
#property indicator_width1  3
#property indicator_level1  0
#property indicator_level2  0.20
#property indicator_level3  -0.20
#property indicator_levelstyle STYLE_DOT
#property indicator_levelwidth 0
#property indicator_levelcolor clrBlack
#property indicator_type1   DRAW_HISTOGRAM
#property indicator_color1  clrBlue
input int                 periods=20; // Periods
input ENUM_APPLIED_VOLUME volumeTypeInp=VOLUME_TICK;  // Volume Type
double                    cmfBuffer[];
void OnInit()
{
   SetIndexBuffer(0,cmfBuffer,INDICATOR_DATA);
   IndicatorSetInteger(INDICATOR_DIGITS,5);
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,0);
   IndicatorSetString(INDICATOR_SHORTNAME,"Chaikin Money Flow("+string(periods)+")");
}
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   if(rates_total<periods)
      return(0);
   int initPos = prev_calculated -1;
   if(initPos<0) initPos = 0;
   if(volumeTypeInp==VOLUME_TICK)
   {
      for(int pos = initPos;pos<=rates_total-periods;pos++)
      {
         double sumAccDis = 0;
         long sumVol = 0;
         
         for(int i = 0; i < periods && !IsStopped(); ++i)
         {
            long thisTickVolume = tick_volume[pos+i];
            sumVol += thisTickVolume;
            sumAccDis += AccDis(high[pos+i], low[pos+i], close[pos+i], thisTickVolume);
         }
         cmfBuffer[pos+periods-1] = sumAccDis/sumVol;
      }
   }
   else
   {
      for(int pos = initPos;pos<=rates_total-periods;pos++)
      {
         double sumAccDis = 0;
         long sumVol = 0;
         
         for(int i = 0; i < periods && !IsStopped(); ++i)
         {
            long thisTickVolume = volume[pos+i];
            sumVol += thisTickVolume;
            sumAccDis += AccDis(high[pos+i], low[pos+i], close[pos+i], thisTickVolume);
         }
         
         cmfBuffer[pos+periods-1] = sumAccDis/sumVol;
      }
   }
   return (rates_total-periods-10);
}
double AccDis(double high,double low,double close,long volume)
{
   double res=0;
   
   if(high!=low)
      res=(2*close-high-low)/(high-low)*volume;
   
   return(res);
}

このコードをまとめてコンパイルすると、インジケーターフォルダーにファイルが見つかります。チャートに追加すると、下の図のように表示されます。

CMFInd

ご覧のとおり、このインジケーターはカスタムコードのインジケーターとまったく同じように見え、同じように動作します。システムで何が役立つかに応じて、ニーズに合わせて変更できます。これまでのところ、シンプルな戦略に応じて取引システムで使用できるカスタムCMFインジケーターができました。これについては次のセクションで説明します。


チャイキンマネーフロー戦略

このセクションでは、CMFインジケーターで使用できるいくつかの簡単な戦略を紹介します。これらの戦略は、パフォーマンスと最適化の観点から、適切かつ体系的な方法でテストを実行するためのさまざまな概念とルールに従います。これらの戦略は概念的には単純ですが、自分の好みに合わせて開発し、テストして、どのように役立つかを確認することができます。

次の3つの簡単な戦略を使用します。

  • CMFゼロクロスオーバー戦略
  • CMF買われ過ぎと売られ過ぎ戦略
  • CMFトレンド検証戦略

CMFゼロクロスオーバー戦略

この戦略は非常に簡単です。CMFインジケーターの値を使用して、いつ買うか、いつ売るかを決定します。前回のCMF値がマイナスで、現在または最後の値がプラスの場合、それは買いシグナルです。前のCMF値が正で、現在の値または最後の値が負の場合、その逆が当てはまります。

単純に言うと次のようになります。

前回のCMF<0かつ現在のCMF>0-->購入

前回のCMF>0かつ現在のCMF<0-->売り 

CMFの買われすぎと売られすぎ戦略

この戦略は前回の戦略とは少し異なります。他のレベルを見て、買われすぎまたは売られすぎの領域にあるかどうかを確認します。これらの領域は、時々またはさまざまな機器間で変更される可能性があり、決定する前にインジケーターの動きを視覚的に検査することでそれを確認できます。CMF値が-0.20以下の場合、買いシグナルがトリガーされ、その逆も同様です。CMF値が0.20以上になると、売りシグナルがトリガーされます。

単純に言うと次のようになります。

CMF<=-0.20-->買い

CMF>=0.20-->売り

CMFトレンド検証戦略

この戦略には、シグナルを取得する際に別のアプローチもあります。ゼロクロスオーバーからのシグナルの取得と、トレンドまたは少なくとも価格の上昇または下降の動きを確認できる別のインジケーターを組み合わせます。移動平均とCMFインジケーターを組み合わせて使用します。前回の終値が移動平均の前の値を下回り、同時に現在の売り値が移動平均を上回り、CMFの現在の値がゼロを上回る場合、買いシグナルがトリガーされ、その逆も同様です。前回の終値が移動平均の前の値より上で、同時に現在の入札価格が移動平均より下であり、CMFの現在の値がゼロより下の場合、売りシグナルがトリガーされます。

単純に言うと次のようになります。

prevClose < prevMA、ask > MA、CMF > 0のすべて--> 買う

prevClose > prevMA、bid < MA、CMF < 0のすべて --> 売る

さまざまなコンセプトを試して、これらの各戦略をテストし、最適化し、可能な限りより良い結果を得られるようにしていきます。次のセクションでこれがどのように機能するかを見ていきます。


チャイキンマネーフロー取引システム

このセクションでは、各戦略をコーディングする方法をステップごとにお見せします。その後、それらをテストし、より良い結果を得るためにどのように最適化するかを示しますので、ぜひご期待ください。この非常に興味深い部分をお読みください。戦略のコーディングに入る前に、まずCMFの値をチャートに表示する小さなEAをコーディングします。これはすべてのEAのベースラインになります。これにより、すべての戦略で正確な値を持つ正確なインジケーターを使用していることが確認できます。

このシンプルなEAで次におこなうことは次のとおりです。

EAの入力を設定して、インジケーターを呼び出す関数で使用される希望の期間と出来高の種類を設定します。

input int                 periods=20; // Periods
input ENUM_APPLIED_VOLUME volumeTypeInp=VOLUME_TICK;  // Volume Type

CMF整数変数を宣言します。

int cmf;

OnInit()イベントでは、iCustom関数を使用してカスタムCMFインジケーターを呼び出して初期化します。パラメータは次のとおりです。

  • Symbol:ここで銘柄名を文字列として入力します。現在の銘柄に適用されるのは(_Symbol)になります。
  • Period:ここで、期間をENUM_TIMEFRAMESとして入力します。現在の時間枠を適用するにはPERIOD_CURRENTになります。
  • Name:ここで、ローカルマシン上の正しいパスを使用して呼び出すカスタムインジケーターの名前を入力します。そのため、folder/custom_indicator_nameを入力する必要があります(Chaikin_Money_Flow)。
  • ...:インジケーターの入力パラメータのリスト(存在する場合)をここに入力します。これはperiodsとvolumeTypeInpの入力になります。

次に、初期化が成功した場合、returnでINIT_SUCCEEDEDを戻して、コードの次の部分に移動します。

int OnInit()
  {
   cmf = iCustom(_Symbol,PERIOD_CURRENT,"Chaikin_Money_Flow",periods,volumeTypeInp);
   return(INIT_SUCCEEDED);
  }

OnDeinit()イベントでは、EAが削除されたというメッセージを、初期化解除イベントが発生したときに表示します。

void OnDeinit(const int reason)
  {
   Print("EA is removed");
  }

OnTick()イベントでは、cmfInd[]の配列をdoubleデータ型として宣言します。

double cmfInd[];

CopyBuffer関数を使用して、cmfインジケーターの指定されたバッファのデータを取得します。パラメータは次の通りです。

  • indicator_handle:前に宣言されたdouble型のcmfハンドルを指定する 
  • buffer_num:cmfバッファ番号を指定する(0) 
  • start_pos:開始位置を指定する(0)
  • count:コピーする数を指定する(3)
  • buffer[]:コピーするcmfInd[]配列を指定する
CopyBuffer(cmf,0,0,3,cmfInd);

時系列内の要素にインデックスを付けるには、cmfInd[]にAS_SERIESフラグを設定します。配列[]と成功時にtrueになるフラグをパラメータとして使用できます。

ArraySetAsSeries(cmfInd,true);

CMFの現在の値のdouble変数を定義し、それを5桁の制限で正規化することで定義できます。

double cmfVal = NormalizeDouble(cmfInd[0], 5);

コメント機能を使用して、以前に定義された現在のcmf値をチャートにコメントします。

Comment("CMF value = ",cmfVal);

完全なコードは、以下の1つのコードブロックに記載されています。

//+------------------------------------------------------------------+
//|                                                      CMF_Val.mq5 |
//+------------------------------------------------------------------+
input int                 periods=20; // Periods
input ENUM_APPLIED_VOLUME volumeTypeInp=VOLUME_TICK;  // Volume Type
int cmf;
int OnInit()
  {
   cmf = iCustom(_Symbol,PERIOD_CURRENT,"Chaikin_Money_Flow",periods,volumeTypeInp);
   return(INIT_SUCCEEDED);
  }
void OnDeinit(const int reason)
  {
   Print("EA is removed");
  }
void OnTick()
  {
    double cmfInd[];
    CopyBuffer(cmf,0,0,3,cmfInd);
    ArraySetAsSeries(cmfInd,true);
    double cmfVal = NormalizeDouble(cmfInd[0], 5);
    Comment("CMF value = ",cmfVal);
  }

このコードをコンパイルし、EAとしてチャートに挿入すると、結果は以下と同じになります。

CMF_Val

ご覧のとおり、チャート上のCMF値は、挿入されたインジケーターの値(0.15904)と同じです。これを他のEAや戦略の出発点として使用できます。

CMFゼロクロスオーバー戦略

前述したように、ゼロクロスオーバー戦略をコーディングして、そのクロスオーバーに基づいて取引が自動的に生成されるようにする必要があります。プログラムはすべての新しいバーを継続的にチェックする必要があります。クロスオーバーがゼロを超えて発生した場合はEAで買い注文を出し、ゼロ未満で発生した場合は売り注文を出す必要があります。

目的を果たす完全なコードは次のとおりです。
//+------------------------------------------------------------------+
//|                                           CMF_Zero_Crossover.mq5 |
//+------------------------------------------------------------------+
#include <trade/trade.mqh>
input int                 periods=20; // Periods
input ENUM_APPLIED_VOLUME volumeTypeInp=VOLUME_TICK;  // Volume Type
input double cmfPosLvls = 0.20; // CMF OB Level
input double cmfNegLvls = -0.20; // CMF OS Level
input int maPeriodInp=20; //MA Period
input double      lotSize=1;
input double      slLvl=300;
input double      tpLvl=900;
int cmf;
CTrade trade;
int barsTotal;
int OnInit()
  {
   barsTotal=iBars(_Symbol,PERIOD_CURRENT);
   cmf = iCustom(_Symbol,PERIOD_CURRENT,"Chaikin_Money_Flow",maPeriodInp,volumeTypeInp);
   return(INIT_SUCCEEDED);
  }
void OnDeinit(const int reason)
  {
   Print("EA is removed");
  }
void OnTick()
  {
   int bars=iBars(_Symbol,PERIOD_CURRENT);
   if(barsTotal != bars)
     {
      barsTotal=bars;
      double cmfInd[];
      CopyBuffer(cmf,0,0,3,cmfInd);
      ArraySetAsSeries(cmfInd,true);
      double cmfVal = NormalizeDouble(cmfInd[0], 5);
      double cmfPreVal = NormalizeDouble(cmfInd[1], 5);
      double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
      double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
      if(cmfPreVal<0 && cmfVal>0)
        {
         double slVal=ask - slLvl*_Point;
         double tpVal=ask + tpLvl*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }
      if(cmfPreVal>0 && cmfVal<0)
        {
         double slVal=bid + slLvl*_Point;
         double tpVal=bid - tpLvl*_Point;
         trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
	}
     }
  }
//+------------------------------------------------------------------+

ご参考までに、このコードには違いがあります。

取引関数を呼び出すために取引ファイルを含めます。

#include <trade/trade.mqh>

ユーザーが決定する入力項目を追加します。売られすぎレベル(cmfPosLvls)、買われすぎレベル(cmfNegLvls)、ロットサイズ、ストップロス、テイクプロフィットです。

input double cmfPosLvls = 0.20; // CMF OB Level
input double cmfNegLvls = -0.20; // CMF OS Level
input double      lotSize=1;
input double      slLvl=300;
input double      tpLvl=900;

取引オブジェクトとbarsTotal整数変数を宣言します。

CTrade trade;
int barsTotal;

OnTick()イベントでは、コードの実行を継続するために新しいバーがあるかどうかを確認するだけです。これは、barsという整数変数を定義した後、if条件を使用して実行できます。

int bars=iBars(_Symbol,PERIOD_CURRENT);
if(barsTotal != bars)

新しいバーがある場合は、次の違いを加えて残りのコードを実行する必要があります。

barsTotalをbarsと等しくなるように更新します。

barsTotal=bars;

前回のCMF値を宣言します。

double cmfPreVal = NormalizeDouble(cmfInd[1], 5);

double型のask変数とbid変数を宣言します。

double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);

買いの条件を「cmfPreVal<0かつcmfVal>0」に設定し、条件を満たしたら、SLとTPを宣言して買い注文を出す必要があります。

      if(cmfPreVal<0 && cmfVal>0)
        {
         double slVal=ask - slLvl*_Point;
         double tpVal=ask + tpLvl*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }

売りの条件を「cmfPreVal>0かつcmfVal<0」に設定し、条件を満たしたら、SLとTPを宣言して売り注文を出す必要があります。

      if(cmfPreVal>0 && cmfVal<0)
        {
         double slVal=bid + slLvl*_Point;
         double tpVal=bid - tpLvl*_Point;
         trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
        }

このコードをまとめてチャートに添付して実行すると、テスト段階の以下の例と同じようにポジションが配置されていることがわかります。

買いポジション

買う

売りポジション

売る

ここで、2023年1月1日から2023年12月31日までの1年間、EURUSDですべての戦略をテストする必要があります。SLに300ポイント、TPに900ポイントを使用します。最適化に対する私たちの主なアプローチは、別の概念をテストするか、移動平均などの別のツールを追加することです。他の時間枠をテストして、どの時間枠のパフォーマンスが優れているかを確認することもできます。このタイプの最適化も試してみる価値があります。

戦略テストの結果を比較する際には、次の主要な測定に焦点を当てます。

  • 純利益:売上総利益から売上総損失を差し引くことで算出。高いほど良い。
  • バランスDD(相対):取引中に口座が経験する最大損失額。低いほど良い。
  • プロフィットファクター:売上総利益と売上総損失の比率。高いほど良い。
  • 期待損益:取引の平均損益。高いほど良い。
  • リカバリーファクター:テストされたストラテジーが損失後にどの程度回復するかを測定するもの。高いほど良い。
  • シャープレシオ:リスクフリーリターンとリターンを比較することで、テストされた取引システムのリスクと安定性を判断するもの。高いほど良い。

15分間時間枠テストの結果を以下に示します。

15分バックテスト1

15分バックテスト2

15分バックテスト3

15分間テストの結果では次の重要な値がわかります。

  • 純利益:29019.10米ドル
  • バランスDD(相対):23%
  • プロフィットファクター:1.09
  • 期待損益:19.21
  • リカバリーファクター:0.88
  • シャープレシオ:0.80

この戦略は15分間の時間枠で利益を上げることができるように見えますが、ドローダウンが大きいため、リスクが増大する可能性があります。そこで、異なるコンセプトの別の戦略、つまりCMFの買われすぎと売られすぎを試してみます。

CMFの買われすぎと売られすぎ戦略

前述したように、自動的に買われ過ぎと売られ過ぎの領域に近づいていることに基づいて、EAによって買い注文と売り注文を出すようにこの戦略をコーディングする必要があります。注目している領域は、売られ過ぎの場合は0.20、売られ過ぎの場合は-0.20です。EAは各CMF値を監視してこれらの領域と比較し、CMF値が-0.20以下の場合は買い注文を出し、CMF値が0.20以上の場合は売り注文を出す必要があります。

この戦略またはEAを作成する手順は、次の完全なコードで確認できます。

//+------------------------------------------------------------------+
//|                                                 CMF_MA_OB&OS.mq5 |
//+------------------------------------------------------------------+
#include <trade/trade.mqh>
input int                 periods=20; // Periods
input ENUM_APPLIED_VOLUME volumeTypeInp=VOLUME_TICK;  // Volume Type
input double cmfPosLvls = 0.20; // CMF OB Level
input double cmfNegLvls = -0.20; // CMF OS Level
input double      lotSize=1;
input double      slLvl=300;
input double      tpLvl=900;
int cmf;
CTrade trade;
int barsTotal;
int OnInit()
  {
   barsTotal=iBars(_Symbol,PERIOD_CURRENT);
   cmf = iCustom(_Symbol,PERIOD_CURRENT,"Chaikin_Money_Flow",periods,volumeTypeInp);
   return(INIT_SUCCEEDED);
  }
void OnDeinit(const int reason)
  {
   Print("EA is removed");
  }
void OnTick()
  {
   int bars=iBars(_Symbol,PERIOD_CURRENT);
   if(barsTotal != bars)
     {
      barsTotal=bars;
      double cmfInd[];
      CopyBuffer(cmf,0,0,3,cmfInd);
      ArraySetAsSeries(cmfInd,true);
      double cmfVal = NormalizeDouble(cmfInd[0], 5);
      double cmfPreVal = NormalizeDouble(cmfInd[1], 5);
      double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
      double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
      if(cmfVal<=cmfNegLvls)
        {
         double slVal=ask - slLvl*_Point;
         double tpVal=ask + tpLvl*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }
      if(cmfVal>=cmfPosLvls)
        {
         double slVal=bid + slLvl*_Point;
         double tpVal=bid - tpLvl*_Point;
         trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
        }
     }
  }
//+------------------------------------------------------------------+

このコードの違いは以下のようになります。

ユーザーの希望に応じて、買われすぎ領域と売られすぎ領域の2つの入力を設定します。とりあえずデフォルト値(0.20と-0.20)を使用します。

input double cmfPosLvls = 0.20; // CMF OB Level
input double cmfNegLvls = -0.20; // CMF OS Level

戦略の条件

CMF値がcmfNegLvls以下の場合は買いポジションを出します。

      if(cmfVal<=cmfNegLvls)
        {
         double slVal=ask - slLvl*_Point;
         double tpVal=ask + tpLvl*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }

CMF値がcmfPosLvls以上の場合は売りポジションを出します。

      if(cmfVal>=cmfPosLvls)
        {
         double slVal=bid + slLvl*_Point;
         double tpVal=bid - tpLvl*_Point;
         trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
        }

このコードをまとめてチャートで実行すると、テスト段階の例と同様に、ポジションが戦略条件と一致している場所を確認できます。

買いポジション

買う

売りポジション

売る

前回のCMFゼロクロスオーバー戦略のテストで使用したのと同じアプローチを使用して、この戦略をテストします。2023年1月1日から2023年12月31日まで、15分の時間枠でEURUSDをSL300ポイント、TP900ポイントでテストします。次の図はこのテストの結果を示しています。

15分バックテスト1

15分バックテスト2

15分バックテスト3

15分間テストの結果では次の重要な値がわかります。

  • 純利益:58029.90米ドル
  • バランスDD(相対):55.60%
  • プロフィットファクター:1.06
  • 期待損益:14.15
  • リカバリーファクター:0.62
  • シャープレシオ:0.69

より高いドローダウンでより多くの利益が見られるようになったので、トレンド検証のために別のテクニカル指標をCMFゼロクロスオーバーに追加または組み合わせて最適化を試みます。次の戦略で何が起こるかを見てみましょう。

CMFトレンド検証戦略

前述したように、両方向(上と下)の動きを検証するには、別のテクニカル指標である移動平均をCMFゼロクロスオーバーに追加または組み合わせる必要があります。EAはすべての終値、売り値、CMFをチェックする必要があります。この値は、各要素の他の要素に対する相対的な位置を決定するために使用されます。最後の終値が最後の移動平均値を下回り、現在の売り値が現在の移動平均を上回り、CMF値がゼロを上回る場合、買い注文を出す必要があります。一方、最終終値が最終移動平均値を上回り、現在の入札価格が現在の移動平均を下回り、CMF値がゼロを下回る場合は、売り注文を出す必要があります。

このEAの完全なコードは次のとおりです。

//+------------------------------------------------------------------+
//|                                          CMF_trendValidation.mq5 |
//+------------------------------------------------------------------+
#include <trade/trade.mqh>
input int                 periods=20; // Periods
input ENUM_APPLIED_VOLUME volumeTypeInp=VOLUME_TICK;  // Volume Type
input int maPeriodInp=20; //MA Period
input double      lotSize=1;
input double      slLvl=300;
input double      tpLvl=900;
int cmf;
int ma;
CTrade trade;
int barsTotal;
int OnInit()
  {
   barsTotal=iBars(_Symbol,PERIOD_CURRENT);
   cmf = iCustom(_Symbol,PERIOD_CURRENT,"Chaikin_Money_Flow",periods,volumeTypeInp);
   ma = iMA(_Symbol,PERIOD_CURRENT,maPeriodInp,0,MODE_SMA,PRICE_CLOSE);
   return(INIT_SUCCEEDED);
  }
void OnDeinit(const int reason)
  {
   Print("EA is removed");
  }
void OnTick()
  {
   int bars=iBars(_Symbol,PERIOD_CURRENT);
   if(barsTotal != bars)
     {
      barsTotal=bars;
      double cmfInd[];
      double maInd[];
      CopyBuffer(cmf,0,0,3,cmfInd);
      CopyBuffer(ma,0,0,3,maInd);
      ArraySetAsSeries(cmfInd,true);
      ArraySetAsSeries(maInd,true);
      double cmfVal = NormalizeDouble(cmfInd[0], 5);
      double maVal= NormalizeDouble(maInd[0],5);
      double cmfPreVal = NormalizeDouble(cmfInd[1], 5);
      double maPreVal = NormalizeDouble(maInd[1],5);;
      double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
      double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
      double prevClose = iClose(_Symbol,PERIOD_CURRENT,1);
      if(prevClose<maPreVal && ask>maVal)
        {
         if(cmfVal>0)
           {
            double slVal=ask - slLvl*_Point;
            double tpVal=ask + tpLvl*_Point;
            trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
           }
        }
      if(prevClose>maPreVal && bid<maVal)
        {
         if(cmfVal<0)
           {
            double slVal=bid + slLvl*_Point;
            double tpVal=bid - tpLvl*_Point;
            trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
           }
        }
     }
  }
//+------------------------------------------------------------------+

このコードの主な違いは次のとおりです。

ユーザーが使用する移動平均の期間を決定するための別の入力を追加します。

input int maPeriodInp=20; //MA Period

移動平均の整数変数を宣言します。

int ma;

iMA関数を使用してma作成変数を定義します。パラメータは次のとおりです。

  • symbol:銘柄名を設定すると、現在の銘柄に(_Symbol)が適用されます。 
  • period:移動平均の期間を設定します。これは、PERIOD_CURRENTで現在使用されている時間枠に適用されます。
  • ma_period:平均期間を設定します。これは、ユーザー入力maPeriodInpになります。
  • ma_shift:必要に応じて水平シフトを設定します。
  • ma_method:平滑化タイプまたは移動平均タイプを指定します。単純なMAを使用する場合はMODE_SMAになります。
  • applied_price:価格の種類を指定する場合はPRICE_CLOSEになります。
ma = iMA(_Symbol,PERIOD_CURRENT,maPeriodInp,0,MODE_SMA,PRICE_CLOSE);

maInd[]配列を宣言します。

double maInd[];

CopyBuffer関数を使用して、MAインジケーターの指定されたバッファのデータを取得します。

CopyBuffer(ma,0,0,3,maInd);

時系列内の要素のインデックスに、maInd[]にAS_SERIESフラグを設定します。

ArraySetAsSeries(maInd,true);

現在のMA値と以前のMA値を定義します。

double maVal= NormalizeDouble(maInd[0],5);
double prevClose = iClose(_Symbol,PERIOD_CURRENT,1);

買いポジションの条件は(prevClose<maPreValかつask>maValかつcmfVal>0)です。

      if(prevClose<maPreVal && ask>maVal)
        {
         if(cmfVal>0)
           {
            double slVal=ask - slLvl*_Point;
            double tpVal=ask + tpLvl*_Point;
            trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
           }
        }

売りポジションの条件は(prevClose>maPreValかつbid<maValかつcmfVal<0)です。

      if(prevClose>maPreVal && bid<maVal)
        {
         if(cmfVal<0)
           {
            double slVal=bid + slLvl*_Point;
            double tpVal=bid - tpLvl*_Point;
            trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
           }
        }

このEAを実行すると、以下の図に示すように、出された注文を見つけることができます。

買いポジション

買う

売りポジション:

売る

この戦略は、CMFゼロクロスオーバー、OBおよびOS戦略の以前のテストと同じアプローチを使用してテストし、2023年1月1日から2023年12月31日まで、SLに300ポイント、TPに900ポイントで15分の時間枠でEURUSDをテストします。次の数字はこのテストの結果です。

15分バックテスト1

15分バックテスト2

15分バックテスト3

15分間テストの結果では次の重要な値がわかります。

  • 純利益:40723.80米ドル
  • バランスDD(相対):6.30%
  • プロフィットファクター:1.91
  • 期待損益:167.59
  • リカバリーファクター:3.59
  • シャープレシオ:3.90

各戦略の結果を見ると、主要な測定の点ではCMFトレンド検証が最適であることは明らかです。


結論

この記事を通じて見てきたように、出来高はトレーディングにおいて非常に重要な概念であり、特に他の有用なツールと組み合わせることで、その効果がさらに高まります。また、戦略をテストする際には、わずかな変更によって結果が大きく左右される可能性があるため、最適化が極めて重要なプロセスであることも理解していただけたかと思います。時間足、ストップロス(SL)、テイクプロフィット(TP)など、さまざまな要素を変更しながら、他のツールを組み合わせてテストをおこない、妥当な結果が得られるまで検証を重ねることを強くおすすめします。

本記事では、チャイキンマネーフロー出来高インジケーターの使い方、カスタマイズ方法、コードの作成手順、さらにシンプルな複数の戦略に基づいたEAの構築・最適化・テスト方法を学んでいただきました。

  • CMFゼロクロスオーバー戦略
  • CMFOBおよびOS戦略
  • CMFトレンド検証戦略

また、それぞれの戦略について、最適化とテスト結果に基づき、どの戦略がより効果的であるかをご確認いただけたかと思います。この記事が皆様のお役に立てれば幸いです。さらに、人気のテクニカル指標を使った取引システムの構築など、私の他の記事にもご興味があれば、出版ページからご覧になり、ご注文ください。

添付のソースコードファイルは以下の通りです。

ファイル名 詳細
Chaikin_Money_Flow 私たちが作成したカスタムCMFインジケーター
CMF_Zero_Crossover ゼロクロスオーバー取引戦略を備えたCMFのEA
CMF_OBOS 買われ過ぎと売られ過ぎの取引戦略を備えたCMFのEA
CMF_trendValidation 動き/トレンド検証取引戦略を備えたCMFのEA


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

添付されたファイル |
CMF_OBeOS.mq5 (1.72 KB)
スイングエントリーモニタリングEAの開発 スイングエントリーモニタリングEAの開発
年末が近づくと、多くの長期トレーダーは市場の過去を振り返り、その動きや傾向を分析して、将来の動向を予測しようとします。この記事では、MQL5を用いて長期エントリーの監視をおこなうエキスパートアドバイザー(EA)の開発について解説します。手動取引や自動監視システムの不在によって、長期的な取引チャンスを逃してしまうという課題に取り組むことが本稿の目的です。今回は、特に取引量の多い通貨ペアの一つを例に挙げ、効果的な戦略を立案しながらソリューションを構築していきます。
MQL5でのファイル操作の習得:基本的なI/OからカスタムCSVリーダーの構築まで MQL5でのファイル操作の習得:基本的なI/OからカスタムCSVリーダーの構築まで
この記事では、取引ログ、CSVの処理、外部データの統合など、MQL5における基本的なファイル操作テクニックに焦点を当て、概念的な理解と実践的なコーディングガイドの両面から解説します。読者は、カスタムCSVインポート用のクラスを段階的に構築する方法を学び、実践的なスキルを身につけることができます。
プライスアクション分析ツールキットの開発(第6回):Mean Reversion Signal Reaper プライスアクション分析ツールキットの開発(第6回):Mean Reversion Signal Reaper
いくつかの概念は一見するとシンプルに思えるかもしれませんが、実際にそれを形にするのは想像以上に難しいことがあります。この記事では、平均回帰(Mean Reversion)戦略を用いて市場を巧みに分析するエキスパートアドバイザー(EA)の自動化に取り組んだ、革新的なアプローチをご紹介します。この魅力的な自動化プロセスの奥深さを、一緒に紐解いていきましょう。
ニュース取引が簡単に(第6回):取引の実施(III) ニュース取引が簡単に(第6回):取引の実施(III)
この記事では、IDに基づいて個々のニュースイベントをフィルターする関数を実装します。さらに、以前のSQLクエリを改善し、追加情報が提供されたり、クエリの実行時間が短縮されるようになります。さらに、これまでの記事で作成したコードを機能的なものにします。