English Deutsch
preview
ケルトナーチャネル取引システムの構築とテスト

ケルトナーチャネル取引システムの構築とテスト

MetaTrader 5トレーディング | 30 4月 2024, 09:00
319 0
Mohamed Abdelmaaboud
Mohamed Abdelmaaboud

はじめに

金融市場におけるボラティリティの概念を理解し、市場で取引する際に有利に働くようにすることは非常に重要です。この記事の主な目的は、ケルトナーチャネルテクニカル指標を使用した、ボラティリティの概念に基づく取引システムの多くのテスト結果を提供することで、この重要な概念に関する特定の条件下で、ボラティリティを測定したり行動を起こしたりするための完全なシステムの一部として、ケルトナーチャネルがどのように機能するかを知ることで、時間を節約することです。

理解すべき最も重要なことは、より良い結果を得るためには、自分の好みに応じてシステムを最適化する必要があるかもしれないということです。自分の取引目的に応じて、自分のシステムに最適な究極の設定を見つけるために、多くの異なる側面をテストする必要があります。

この記事では、ケルトナーチャネル指標と取引システムを作成し、シンプルな取引戦略をおこなう方法を学び、さまざまな金融資産でそれらをテストし、テストと最適化によってどれがより良い結果を出せるかを比較し、可能な限り最良の設定を提供することを目指します。この記事では、以下のトピックを取り上げます。

上記のトピックをカバーすれば、ケルトナーチャネル指標をその主な概念に基づいて使用できるようになります。指標を詳細に理解した後は、シンプルな戦略に基づいて取引システムを作成できるようになります。その後、それらをテストし、その結果に基づいてどの戦略が他より優れているかを見ることができるでしょう。この記事が、読者が取引を改善して新しいことを学んでより良い結果を得るのに役立つこと、または取引結果を向上させる関連アイデアについてさらに洞察を得るのに役立つことを願っています。
免責条項:すべての情報は「現状有姿」で提供され、情報提供のみを目的としており、取引目的やアドバイスを目的としたものではありません。いかなる結果も保証するものではありません。読者がこれらの資料を自分の取引口座で使用する場合、自己責任でおこなってください。


ボラティリティの定義

このセクションでは、ボラティリティとは一般的にどのようなもので、取引や金融市場においてどのように重要なのかを理解します。ボラティリティは、金融資産のリターンの分散を測定するために使用できる統計的概念であり、ボラティリティが上昇するとリスクも上昇します。ボラティリティとは、相場が双方向に大きく振れることを指す概念であり、この振れ幅は平均価格を中心とした振れ幅で測定されます。

市場の乱高下には、以下のような多くの理由があります。

  • 市場のセンチメント:市場のボラティリティはトレーダーの感情に影響されることがあります。
  • 決算:決算報告は、その結果に基づいて市場のボラティリティに影響を与える可能性があります。
  • 経済イベントと経済指標:インフレ、GDP、雇用統計など、重要な経済イベントや経済指標も市場の変動につながります。
  • 流動性:市場の流動性とボラティリティには関係があり、流動性が低いとボラティリティは上昇します。

ボラティリティの測定に関しては、ベータ係数や標準偏差など、さまざまな方法や手法があります。また、ボリンジャーバンド、アベレージトゥルーレンジ、ボラティリティインデックス(VIX)、そして今回のテーマであるケルトナーチャネルなど、市場のボラティリティを測定するために使用できるテクニカル指標も数多くあります。ボラティリティの測定は、金融資産の変動を評価するのに非常に役立ちます。

また、インプライドボラティリティ(予想変動率)やヒストリカルボラティリティなど、ボラティリティには多くの種類があります。


ケルトナーチャンネルの定義

このセクションでは、ケルトナーチャネル指標の詳細、使用方法、そしてこの指標の計算方法について学び、この指標の有利な使用方法を理解します。ケルトナーチャネルは、価格の上下に2つのバンドとその間に移動平均を含むボラティリティ指標です。

ケルトナーチャネルは、1960年代にチェスター・ケルトナーが著書「How to Make Money in Commodities」の中で初めて紹介した指標です。計算は単純移動平均と高値安値の範囲を使用していましたが、現在一般的に使用されている、ATR(アベレージトゥルーレンジ)を使用する形に進化しました。移動平均の典型的な設定は20期間であり、上下のバンドはEMAの上下にATRの2倍として計算されます。これらの設定はユーザーの好みや取引目的に応じて調整することができます。

ケルトナーチャネルは、トレンドの方向性を識別するために使用できるため、価格が上限バンドに達すると強気、下限バンドに達すると弱気を指します。バンドはまた、価格が明確な上昇トレンドや下降トレンドを伴わずにその間を動くときに、支持や抵抗として使用することもできます。要約すると、ケルトナーチャネルは、指数移動平均とATRを用いて金融資産のボラティリティを測定するテクニカル指標です。 

ケルトナーチャネルの計算方法を以下に示します。

  • 指数移動平均を計算すると、指標の真ん中の線になります。
  • ATRを計算し、バンド計算に使用します。
  • 上限バンドをEMAと等しくなるように計算し、それに2とATRを乗算したものを加算します。
  • EMAと等しくなるように下限バンドを計算し、2とATRを乗算したものを減算します。

つまり

ケルトナーチャネル中央線 = 指数移動平均(EMA)

ケルトナーチャネルの上限バンド = 指数移動平均(EMA) + 2 * ATR

ケルトナーチャネルの下限バンド = 指数移動平均(EMA) - 2 * ATR


ケルトナーチャネル戦略

ケルトナーチャネル指標の使い方として、バンドを支持や抵抗として使用したり、上側のバンドに近づいたら強気、下側のバンドに近づいたら弱気と考える方法について理解したことを踏まえて、2つの簡単な戦略を使用します。

  • バンド反発:下限バンドより上で反発したときに買い注文を出し、上限バンドより下で反発したときに売り注文を出す
  • バンドブレイクアウト:上のバンドをブレイクしたら買い注文を出し、下のバンドをブレイクしたら売り注文を出す

戦略1:バンド反発

ケルトナーチャネル指標について理解したところによると、この指標は、終値と指標のバンドの位置に基づいて売買シグナルを検出することで使用できます。前回の終値が前回の下限バンドを下回り、同時に前回の終値が下限バンドを上回ったら買い注文を出します。売り注文は、直前の終値が上限バンドより上で寄り付き、同時に直前の終値が上限バンドより下で寄り付いたときに発注されます。

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

直近終値 < 下限バンド、かつ、直近終値 > 下限バンド==>買いシグナル

直近終値 > 上限バンド、かつ、直近終値 < 上限バンド== > 売りシグナル

戦略2:バンドブレイクアウト
この戦略では、ブレイクアウトが発生する間に注文を出します。前回の終値が前回の上限バンドを下回り、同時に前回の終値が前回の上限バンドを上回った時点で買い注文を出し、逆に前回の終値が前回の下限バンドを上回り、同時に前回の終値が前回の下限バンドを下回った時点で売り注文を出します。

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

直近終値 < 上限バンド、かつ直近終値 > 上限バンド==>買いシグナル

直近終値 > 下限バンド、かつ、直近終値 < 下限バンド== > 売りシグナル


ケルトナーチャネル取引システム

このセクションでは、先に述べた戦略に基づいて自動的に注文を出す取引システムを作成します。まず、カスタム指標を作成して、後で取引システムを作成するために使用するケルトナーチャネル指標を計算します。指標をコーディングする方法については、次のとおりです。

#propertyプリプロセッサを使用して指標の場所を決定し、indicator_chart_windowを使用してチャート上に指標を表示します。

#property indicator_chart_window

iindicator_buffersの識別子の値を使用してバッファ指標を決定するには、数を3に設定します。プロットの数を決定するには、同じく3を設定します。

#property indicator_buffers 3
#property indicator_plots 3

指標のタイプ、スタイル、幅、色、ラベルのプロパティを上中下の線に設定します。

#property indicator_type1 DRAW_LINE
#property indicator_style1 STYLE_SOLID
#property indicator_width1 2
#property indicator_color1 clrRed
#property indicator_label1 "Keltner Upper Band"


#property indicator_type2 DRAW_LINE
#property indicator_style2 STYLE_SOLID
#property indicator_width2 1
#property indicator_color2 clrBlue
#property indicator_label2 "Keltner Middle Line"


#property indicator_type3 DRAW_LINE
#property indicator_style3 STYLE_SOLID
#property indicator_width3 2
#property indicator_color3 clrGreen
#property indicator_label3 "Keltner Lower Band"

移動平均の期間、チャネル倍率、バンド計算におけるATRの使用、移動平均の方法またはモード、価格の種類など、指標の入力を設定します。

input int      maPeriod            = 10;           // Moving Average Period
input double   multiInp             = 2.0;          // Channel multiplier
input bool     isAtr                 = false;        // ATR
input ENUM_MA_METHOD     maMode       = MODE_EMA;     // Moving Average Mode
input ENUM_APPLIED_PRICE priceType = PRICE_TYPICAL;// Price Type

グローバル変数として、3つ(上中下)の配列、移動平均とATRの2つのハンドル、minBarsを宣言します。

double upper[], middle[], lower[];
int maHandle, atrHandle;
static int minBars = maPeriod + 1;

OnInit()関数では、SetIndexBuffer関数を使用して、指標バッファと配列をリンクします。

  • index:インデックスバッファ(上:0、中:1、下:2)
  • buffer[]:配列(上、中、下)
  • data_type:指標に格納するもの(ENUM_INDEXBUFFER_TYPEのいずれか)
   SetIndexBuffer(0,upper,     INDICATOR_DATA);
   SetIndexBuffer(1,middle,  INDICATOR_DATA);
   SetIndexBuffer(2,lower,  INDICATOR_DATA);

配列にAS_Seriesフラグを設定するには、ArraySetAsSeries関数とそのパラメータを使用します。

  • array[]:配列を参照によって決定(上中下の配列を使用)
  • flag:配列のインデックスの方向(成功した場合はtrueを、失敗した場合はfalseを返す)
   ArraySetAsSeries(upper, true);
   ArraySetAsSeries(middle, true);
   ArraySetAsSeries(lower, true);

IndicatorSetString関数とそのパラメータを使用して指標の名前を設定します。

  • prop_id:識別子(ENUM_CUSTOMIND_PROPERTY_STRINGのいずれか)。INDICATOR_SHORTNAMEを使用して指標銘を設定
  • prop_value:目的の指標名
IndicatorSetString(INDICATOR_SHORTNAME,"Custom Keltner Channel " + IntegerToString(maPeriod));

IndicatorSetInteger関数とそのパラメータを使用して指標値の桁を設定することは、prop_valueのデータ型が文字列の代わりになることを除いて、IndicatorSetStringと同じです。

IndicatorSetInteger(INDICATOR_DIGITS,_Digits);

iMA関数とそのパラメータを使用して移動平均ハンドルを定義します。

  • symbol:銘柄名(現在の銘柄に適用されるNULLを使用)
  • period:期間(現在の期間に0を使用)
  • ma_period:移動平均の期間(maPeriodのユーザー入力を使用)
  • ma_shift:水平方向のシフト(オプション)
  • ma_method:移動平均の方法(maModeのユーザー入力を使用)
  • applied_price:価格タイプ(priceTypeのユーザー入力を使用)
maHandle = iMA(NULL, 0, maPeriod, 0, maMode, priceType);

ATRの入力がtrueかfalseかに応じてATRを定義し、バンド計算に使用するかどうかを決定します。

   if(isAtr)
     {
      atrHandle = iATR(NULL, 0, maPeriod);
      if(atrHandle == INVALID_HANDLE)
        {
         Print("Handle Error");
         return(INIT_FAILED);
        }
     }
   else
      atrHandle = INVALID_HANDLE;

指標バッファ、移動平均、中間値、上限値、下限値を指定するindValue関数を宣言します。

void indValue(const double& h[], const double& l[], int shift)
  {
   double ma[1];
   if(CopyBuffer(maHandle, 0, shift, 1, ma) <= 0)
      return;
   middle[shift] = ma[0];
   double average = AVG(h, l, shift);
   upper[shift]    = middle[shift] + average * multiInp;
   lower[shift] = middle[shift] - average * multiInp;
  }

計算された乗数に基づいて境界の位置を計算するAVG関数を宣言します。

double AVG(const double& High[],const double& Low[], int shift)
  {
   double sum = 0.0;
   if(atrHandle == INVALID_HANDLE)
     {
      for(int i = shift; i < shift + maPeriod; i++)
         sum += High[i] - Low[i];
     }
   else
     {
      double t[];
      ArrayResize(t, maPeriod);
      ArrayInitialize(t, 0);
      if(CopyBuffer(atrHandle, 0, shift, maPeriod, t) <= 0)
         return sum;
      for(int i = 0; i < maPeriod; i++)
         sum += t[i];
     }
   return sum / maPeriod;
  }

OnCalculate関数では、以下のコードのように指標の値を計算します。

   if(rates_total <= minBars)
      return 0;
   ArraySetAsSeries(close,true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low,  true);
   int limit = rates_total - prev_calculated;
   if(limit == 0)             
     {
     }
   else
      if(limit == 1)      
        {
         indValue(high, low, 1);
         return(rates_total);
        }
      else
         if(limit > 1)       
           {
            ArrayInitialize(middle, EMPTY_VALUE);
            ArrayInitialize(upper,    EMPTY_VALUE);
            ArrayInitialize(lower, EMPTY_VALUE);
            limit = rates_total - minBars;
            for(int i = limit; i >= 1 && !IsStopped(); i--)
               indValue(high, low, i);
            return(rates_total);
           }
   indValue(high, low, 0);
   return(rates_total);

ケルトナーチャネルのカスタム指標の完全なコードは、以下の1つのブロックになります。

//+------------------------------------------------------------------+
//|                                       Custom_Keltner_Channel.mq5 |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_buffers 3
#property indicator_plots 3
#property indicator_type1 DRAW_LINE
#property indicator_style1 STYLE_SOLID
#property indicator_width1 2
#property indicator_color1 clrRed
#property indicator_label1 "Keltner Upper Band"
#property indicator_type2 DRAW_LINE
#property indicator_style2 STYLE_SOLID
#property indicator_width2 1
#property indicator_color2 clrBlue
#property indicator_label2 "Keltner Middle Line"
#property indicator_type3 DRAW_LINE
#property indicator_style3 STYLE_SOLID
#property indicator_width3 2
#property indicator_color3 clrGreen
#property indicator_label3 "Keltner Lower Band"
input int      maPeriod            = 10;           // Moving Average Period
input double   multiInp             = 2.0;          // Channel multiplier
input bool     isAtr                 = false;        // ATR
input ENUM_MA_METHOD     maMode       = MODE_EMA;     // Moving Average Mode
input ENUM_APPLIED_PRICE priceType = PRICE_TYPICAL;// Price Type
double upper[], middle[], lower[];
int maHandle, atrHandle;
static int minBars = maPeriod + 1;
//+------------------------------------------------------------------+
int OnInit()
  {
   SetIndexBuffer(0,upper, INDICATOR_DATA);
   SetIndexBuffer(1,middle, INDICATOR_DATA);
   SetIndexBuffer(2,lower, INDICATOR_DATA);
   ArraySetAsSeries(upper, true);
   ArraySetAsSeries(middle, true);
   ArraySetAsSeries(lower, true);
   IndicatorSetString(INDICATOR_SHORTNAME,"Custom Keltner Channel " + IntegerToString(maPeriod));
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
   maHandle = iMA(NULL, 0, maPeriod, 0, maMode, priceType);
   if(isAtr)
     {
      atrHandle = iATR(NULL, 0, maPeriod);
      if(atrHandle == INVALID_HANDLE)
        {
         Print("Handle Error");
         return(INIT_FAILED);
        }
     }
   else
      atrHandle = INVALID_HANDLE;
   return INIT_SUCCEEDED;
  }
void indValue(const double& h[], const double& l[], int shift)
  {
   double ma[1];
   if(CopyBuffer(maHandle, 0, shift, 1, ma) <= 0)
      return;
   middle[shift] = ma[0];
   double average = AVG(h, l, shift);
   upper[shift]    = middle[shift] + average * multiInp;
   lower[shift] = middle[shift] - average * multiInp;
  }
double AVG(const double& High[],const double& Low[], int shift)
  {
   double sum = 0.0;
   if(atrHandle == INVALID_HANDLE)
     {
      for(int i = shift; i < shift + maPeriod; i++)
         sum += High[i] - Low[i];
     }
   else
     {
      double t[];
      ArrayResize(t, maPeriod);
      ArrayInitialize(t, 0);
      if(CopyBuffer(atrHandle, 0, shift, maPeriod, t) <= 0)
         return sum;
      for(int i = 0; i < maPeriod; i++)
         sum += t[i];
     }
   return sum / maPeriod;
  }
//+------------------------------------------------------------------+
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 <= minBars)
      return 0;
   ArraySetAsSeries(close,true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low,  true);
   int limit = rates_total - prev_calculated;
   if(limit == 0)             
     {
     }
   else
      if(limit == 1)      
        {
         indValue(high, low, 1);
         return(rates_total);
        }
      else
         if(limit > 1)       
           {
            ArrayInitialize(middle, EMPTY_VALUE);
            ArrayInitialize(upper,    EMPTY_VALUE);
            ArrayInitialize(lower, EMPTY_VALUE);
            limit = rates_total - minBars;
            for(int i = limit; i >= 1 && !IsStopped(); i--)
               indValue(high, low, i);
            return(rates_total);
           }
   indValue(high, low, 0);
   return(rates_total);
  }
//+------------------------------------------------------------------+

このコードをコンパイルした後、indicatorフォルダにあるのを確認し、チャートに挿入すると、以下の例のようになります。

KCH_insert

前のチャートでわかるように、ケルトナーチャネル指標を表すチャネルが価格を囲んでいます。次に、この指標を使用した取引システムを、前述の戦略に基づいて作成します。

次に、それぞれの戦略の概念に基づいて売買注文を自動発注する取引システムを作成します。


戦略1:バンド反発取引システム

最初の戦略は、EAを作成して自動的に注文を出すことで、バンド付近の反発に基づいて取引を開始することを検討することです。以下は、この取引システムをどのようにコード化するかです。

プリプロセッサーincludeを使用して取引ファイルをインクルードします。

#include <trade/trade.mqh>

移動平均期間、チャネル倍率、バンド計算のためのATRの有無、移動平均モード、価格タイプ、ロットサイズ、損切りpips、利食いpipsの入力を宣言します。

input int      maPeriod            = 10;           // Moving Average Period
input double   multiInp             = 2.0;          // Channel multiplier
input bool     isAtr                 = false;        // ATR
input ENUM_MA_METHOD     maMode       = MODE_EMA;     // Moving Average Mode
input ENUM_APPLIED_PRICE priceType = PRICE_TYPICAL;// Price Type
input double      lotSize=1;
input double slPips = 150;
input double tpPips = 300;

Keltner、barTotal、trade各オブジェクトのグローバル変数を宣言します。

int keltner;
int barsTotal;
CTrade trade;

OnInit()関数では、iBars関数とそのパラメータを使用して、新しいバーがあるかどうかを評価するために後で使用するbarsTotal変数を定義します。

  • symbol:コードを適用する銘柄を決定し、現在の銘柄に適用されるように_Symbolを使用します。
  • timeframe:期間を決定し、PERIOD_CURRENTを使用して現在の時間枠に適用します。
barsTotal=iBars(_Symbol,PERIOD_CURRENT);

iCustom関数とそのパラメータを使用して、指標ハンドルとして使用するケルトナー変数を定義します。

  • symbol:銘柄名を決定し、_Symblは現在の銘柄に適用されます。
  • 期間:時間枠を設定するには、PERIOD_CURRENTを使用します。
  • name:指標名を指定します。
  • ...:指標入力のリスト。
keltner = iCustom(_Symbol,PERIOD_CURRENT,"Custom_Keltner_Channel",maPeriod,multiInp, isAtr, maMode, priceType);

OnDeinit関数では、EAを削除する際にメッセージを表示します。

Print("EA is removed");

OnTick()関数では、新しいバーがあるかどうかを確認するために、barsTotalと比較されるバーの整数変数を宣言して定義します。

int bars=iBars(_Symbol,PERIOD_CURRENT);

barsTotalがbarsより小さい場合、barsTotalとbarsを比較します。

if(barsTotal < bars)

barsTotalがbarsより小さい場合、barsTotalにbarsの値を代入する必要があります。

barsTotal=bars;

次に、上段、中段、下段の3つの配列を宣言します。

double upper[], middle[], lower[];

CopyBuffer関数とそのパラメータを使用して、指標バッファのデータを取得します。

  • indicator_handle:指標のハンドルを決定するため、上、中、下にケルトナーを使用します。
  • buffer_num:指標のバッファ番号を指定し、上段は0、中段は1、下段は2となります。
  • start_pos:カウントを開始する位置を指定します。
  • count:コピーする量を指定します。ここでは3を使用します。
  • buffer[]:でコピー先の配列を指定し、上段、中段、下段の配列を使用します。
      CopyBuffer(keltner,0,0,3,upper);
      CopyBuffer(keltner,1,0,3,middle);
      CopyBuffer(keltner,2,0,3,lower);

ArraySetAsSeries関数によるAS_SERIESフラグの設定

      ArraySetAsSeries(upper,true);
      ArraySetAsSeries(middle,true);
      ArraySetAsSeries(lower,true);

指標値の最後の値の前の値のprevUpper、middle、lowerの宣言と定義。

      double prevUpperValue = NormalizeDouble(upper[2], 5);
      double prevMiddleValue = NormalizeDouble(middle[2], 5);
      double prevLowerValue = NormalizeDouble(lower[2], 5);

指標の最終値の上限値、中間値、下限値を宣言し、定義します。

      double upperValue = NormalizeDouble(upper[1], 5);
      double middleValue = NormalizeDouble(middle[1], 5);
      double lowerValue = NormalizeDouble(lower[1], 5);

iClose関数とそのパラメータを使用して、終値の最後とその前を宣言し、定義します。

  • symbol:銘柄名を指定します。
  • timeframe:適用する期間を指定します。
  • shift:インデックスを指定して、後方へのシフトが必要かどうかを指定します。
      double lastClose=iClose(_Symbol,PERIOD_CURRENT,1);
      double prevLastClose=iClose(_Symbol,PERIOD_CURRENT,2);

直近の終値が直近の下値より安く、かつ、直近の終値が直近の下値より大きい場合に買い注文を発注する戦略の条件設定。

      if(prevLastClose<prevLowerValue && lastClose>lowerValue)
        {
         double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
         double slVal = lowerValue - slPips*_Point;
         double tpVal = middleValue + tpPips*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }

直近終値が直近上限値より大きく、かつ、直近終値が直近上限値より小さい場合に売り注文を発注する戦略の条件設定。

      if(prevLastClose>prevUpperValue && lastClose<upperValue)
        {
         double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
         double slVal = upperValue + slPips*_Point;
         double tpVal = middleValue - tpPips*_Point;
         trade.Sell(lotSize,_Symbol,bid,upperValue,tpVal);
        }

以下は、バンド反発戦略に基づく取引システムを作成するための、1ブロックの完全なコードです。

//+------------------------------------------------------------------+
//|                                       Keltner_Trading_System.mq5 |
//+------------------------------------------------------------------+
#include <trade/trade.mqh>
input int      maPeriod            = 10;           // Moving Average Period
input double   multiInp             = 2.0;          // Channel multiplier
input bool     isAtr                 = false;        // ATR
input ENUM_MA_METHOD     maMode       = MODE_EMA;     // Moving Average Mode
input ENUM_APPLIED_PRICE priceType = PRICE_TYPICAL;// Price Type
input double      lotSize=1;
input double slPips = 150;
input double tpPips = 300; 
int keltner;
int barsTotal;
CTrade trade;
int OnInit()
  {
   barsTotal=iBars(_Symbol,PERIOD_CURRENT);
   keltner = iCustom(_Symbol,PERIOD_CURRENT,"Custom_Keltner_Channel",maPeriod,multiInp, isAtr, maMode, priceType);
   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 upper[], middle[], lower[];
      CopyBuffer(keltner,0,0,3,upper);
      CopyBuffer(keltner,1,0,3,middle);
      CopyBuffer(keltner,2,0,3,lower);
      ArraySetAsSeries(upper,true);
      ArraySetAsSeries(middle,true);
      ArraySetAsSeries(lower,true);
      double prevUpperValue = NormalizeDouble(upper[2], 5);
      double prevMiddleValue = NormalizeDouble(middle[2], 5);
      double prevLowerValue = NormalizeDouble(lower[2], 5);
      double upperValue = NormalizeDouble(upper[1], 5);
      double middleValue = NormalizeDouble(middle[1], 5);
      double lowerValue = NormalizeDouble(lower[1], 5);
      double lastClose=iClose(_Symbol,PERIOD_CURRENT,1);
      double prevLastClose=iClose(_Symbol,PERIOD_CURRENT,2);
      if(prevLastClose<prevLowerValue && lastClose>lowerValue)
        {
         double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
         double slVal = lowerValue - slPips*_Point;
         double tpVal = middleValue + tpPips*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }
      if(prevLastClose>prevUpperValue && lastClose<upperValue)
        {
         double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
         double slVal = upperValue + slPips*_Point;
         double tpVal = middleValue - tpPips*_Point;
         trade.Sell(lotSize,_Symbol,bid,upperValue,tpVal);
        }
     }
  }
//+------------------------------------------------------------------+


戦略2:バンドブレイクアウト取引システム

最初の戦略は、EAを作成して自動的に注文を出すことで、バンドを上回るか下回るかによって取引を開始することを検討することです。以下は、この取引システムをコーディングする際の相違点です。

EAが買い注文を出す条件は、まず直近の終値が直近の上限バンドを下回り、次に直近の終値が直近の上限バンドを上回ったときです。

      if(prevLastClose<prevUpperValue && lastClose>upperValue)
        {
         double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
         double slVal = upperValue - slPips*_Point;
         double tpVal = upperValue + tpPips*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }

EAが売り注文を出す条件として、まず直近の終値が直近の下限バンドより上にあることを確認し、次に直近の終値が直近の下限バンドより下にあることを確認します。

      if(prevLastClose>prevLowerValue && lastClose<lowerValue)
        {
         double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
         double slVal = lowerValue + slPips*_Point;
         double tpVal = lowerValue - tpPips*_Point;
         trade.Sell(lotSize,_Symbol,bid,upperValue,tpVal);
        }

以下は、バンド反発戦略に基づく取引システムを作成するための完全なコードです。

//+------------------------------------------------------------------+
//|                                      Keltner_Trading_System2.mq5 |
//+------------------------------------------------------------------+
#include <trade/trade.mqh>
input int      maPeriod            = 10;           // Moving Average Period
input double   multiInp             = 2.0;          // Channel multiplier
input bool     isAtr                 = false;        // ATR
input ENUM_MA_METHOD     maMode       = MODE_EMA;     // Moving Average Mode
input ENUM_APPLIED_PRICE priceType = PRICE_TYPICAL;// Price Type
input double      lotSize=1;
input double slPips = 150;
input double tpPips = 500; 
int keltner;
int barsTotal;
CTrade trade;
int OnInit()
  {
   barsTotal=iBars(_Symbol,PERIOD_CURRENT);
   keltner = iCustom(_Symbol,PERIOD_CURRENT,"Custom_Keltner_Channel",maPeriod,multiInp, isAtr, maMode, priceType);
   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 upper[], middle[], lower[];
      CopyBuffer(keltner,0,0,3,upper);
      CopyBuffer(keltner,1,0,3,middle);
      CopyBuffer(keltner,2,0,3,lower);
      ArraySetAsSeries(upper,true);
      ArraySetAsSeries(middle,true);
      ArraySetAsSeries(lower,true);
      double prevUpperValue = NormalizeDouble(upper[2], 5);
      double prevMiddleValue = NormalizeDouble(middle[2], 5);
      double prevLowerValue = NormalizeDouble(lower[2], 5);
      double upperValue = NormalizeDouble(upper[1], 5);
      double middleValue = NormalizeDouble(middle[1], 5);
      double lowerValue = NormalizeDouble(lower[1], 5);
      double lastClose=iClose(_Symbol,PERIOD_CURRENT,1);
      double prevLastClose=iClose(_Symbol,PERIOD_CURRENT,2);
      if(prevLastClose<prevUpperValue && lastClose>upperValue)
        {
         double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
         double slVal = upperValue - slPips*_Point;
         double tpVal = upperValue + tpPips*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }
      if(prevLastClose>prevLowerValue && lastClose<lowerValue)
        {
         double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
         double slVal = lowerValue + slPips*_Point;
         double tpVal = lowerValue - tpPips*_Point;
         trade.Sell(lotSize,_Symbol,bid,upperValue,tpVal);
        }
     }
  }
//+------------------------------------------------------------------+

では、2023年1月1日から2023年12月31日までの期間について、デフォルト設定の入力で1H時間枠を使用して、金(XAUUSD)、(EURUSD)、(GBPUSD)の2つの取引戦略のこれらのEAをテストします、

両者を比較するために、以下の主要な測定に焦点を当てます。

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


戦略1:バンドの反発テスト

XAUUSDのテスト

(XAUUSD)のテスト結果は以下の通りです。

グラフ

バックテスト

バックテスト2

前回のXAUUSDのテスト結果からわかるように、テストにおいて重要な数値は以下と同じです。

  • 純利益:11918.60 USD.
  • 相対的なバランスDD:3.67%
  • プロフィットファクター:1.36
  • 期待損益:75.91
  • 回収率:2.85
  • シャープレシオ:4.06

EURUSDのテスト

(EURUSD)のテスト結果は以下の通りです。

グラフ

バックテスト

バックテスト2

前回のEURUSDのテスト結果からわかるように、テストにおいて重要な数値は以下の通りです。

  • 純利益:2221.20 USD
  • 相対的なバランスDD:2.86%
  • プロフィットファクター:1.11
  • 期待損益:13.63
  • リカバリーファクター:0.68
  • シャープレシオ:1.09

GBPUSDのテスト

(GBPUSD)のテスト結果は以下の通りです。

グラフ

バックテスト

バックテスト2

GBPUSDのテスト結果からわかるように、テストにおいて重要な数値は以下の通りです。

  • 純利益:-1389.40 USD
  • 相対的なバランスDD:4.56%
  • プロフィットファクター:0.94
  • 期待損益:-8.91
  • リカバリーファクター:-0.28
  • シャープレシオ:-0.78


戦略2:バンドブレイクアウトのテスト

XAUUSDのテスト

(XAUUSD)のテスト結果のグラフは以下の通りです。

グラフ

バックテスト

バックテスト2

前回のXAUUSDのテスト結果からわかるように、テストにおいて重要な数値は以下と同じです。

  • 純利益:-11783 USD
  • 相対的なバランスDD:12.89%
  • プロフィットファクター:0.56
  • 期待損益:-96.58
  • リカバリーファクター:-0.83
  • シャープレシオ:-5.00

EURUSDのテスト

(EURUSD)のテスト結果のグラフは以下の通りです。

グラフ

バックテスト

バックテスト2

前回のEURUSDのテスト結果からわかるように、テストにおいて重要な数値は以下の通りです。

  • 純利益:-1358.30 USD
  • 相対的なバランスDD:6.53%
  • プロフィットファクター:0.94
  • 期待損益:-8.54
  • リカバリーファクター:-0.20
  • シャープレシオ:-0.59

GBPUSDのテスト

(GBPUSD)のテスト結果のグラフは以下の通りです。

グラフ

バックテスト

バックテスト2

GBPUSDのテスト結果からわかるように、テストにおいて重要な数値は以下の通りです。

  • 純利益:-3930.60 USD
  • 相対的なバランスDD:5.06%
  • プロフィットファクター:0.84
  • 期待損益:-25.69
  • リカバリーファクター:-0.75
  • シャープレシオ:-2.05

これまでの戦略で、2つ以上の商品をテストした結果を見てみると、すべての結果を1つの表にまとめて、以下のように簡単に比較することができます。

all_results2

上の表に基づいて、テストされた戦略と同じ時間枠に対応する最高の数字を見つけることができます。
  • 純利益:  最も高い数字(11918.60 USD)は、XAUUSD資産でテストした場合にバンド反発戦略で示されています。
  • バランスDD相対的: 最も低い数値(2.86%)は、EURUSD資産でテストした場合にバンド反発戦略で示されています。
  • プロフィットファクター: 最も高い数字(1.36)は、XAUUSD資産でテストした場合にバンド反発戦略で示されています。
  • 期待損益: より高い数値(75.91)は、XAUUSD資産でテストした場合にバンド反発戦略で示されています。
  • リカバリーファクター: より高い数値(2.85)は、XAUUSD資産でテストした場合にバンド反発戦略で示されています。
  • シャープレシオ: より高い数値(4.06)は、XAUUSD資産でテストした場合にバンド反発戦略で示されています。

見ての通り、最良の戦略はXAUUSDを使用したバンド反発戦略です。より良い結果と設定を見たい場合は、取引目的に応じて戦略を最適化し、それらを再テストしてください。


結論

トレーダーとして、信頼できる取引システムを持つことは非常に重要です。この取引システムがボラティリティなどのあらゆる重要な側面を含み、測定するものであれば、その信頼性は高まります。

本記事では、ケルトナーチャネル指標の詳細、主要概念、計算方法を確認した上で、この重要な概念に基づいた取引システムをどのように取り入れたり使用したりすることができるかを知っていただくために、ケルトナーチャネル指標に基づいた簡単な戦略をテストした結果を提供しました。

以下の戦略を挙げました。

  • バンド反発:下限バンドより上で反発したら買い注文を出し、上限バンドより下で反発したら売り注文を出す。
  • バンドブレイクアウト:上側のバンドをブレイクしたら買い注文を出し、下側のバンドをブレイクしたら売り注文を出す。

これらをテストし、それぞれの戦略の結果に基づいて、多くの金融商品(XAUUSD、EURUSD、GBPUSD)の重要な数値を特定し、それに応じてどちらが優れているかを知ることができました。

先に述べたように、この記事の主な目的は、より良い取引システムを構築または開発するために目を開くための取引アイデアをいくつか共有することによって、何ができるかを共有することです。取引システムのより良い結果を見つけるために、これらの言及された戦略は、より多くの最適化と努力が必要な場合があることを理解することが重要です。

この記事が読者の取引とコーディング学習の旅に役立ち、より良い、読者がより信頼性の高い、より効果的な結果を得られることを願っています。この記事が役に立ち、さまざまな戦略やさまざまなテクニカル指標、特に最も人気のある指標に基づく取引システムの構築についてさらにお読みになりたい場合は、私の出版物ページで、最も人気のあるテクニカル指標に関する多くの記事をご覧になれます。

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

MQL5を使ったシンプルな多通貨エキスパートアドバイザーの作り方(第6回):互いのラインを交差する2つのRSI指標 MQL5を使ったシンプルな多通貨エキスパートアドバイザーの作り方(第6回):互いのラインを交差する2つのRSI指標
この記事の多通貨EAは、クロスラインを持つ2つのRSI指標、低速RSIと交差する高速RSIを使用するEA(自動売買ロボット)です。
アルーン(Aroon)取引システムの構築とテスト アルーン(Aroon)取引システムの構築とテスト
この記事では、指標の基本を学んだ後、どのようにアルーンの取引システムを構築できるかを学び、アルーンの指標に基づいた取引システムを構築するために必要なステップを紹介します。この取引システムを構築した後、利益が出るのかさらに最適化が必要なのかをテストします。
MQL5における修正グリッドヘッジEA(第2部):シンプルなグリッドEAを作る MQL5における修正グリッドヘッジEA(第2部):シンプルなグリッドEAを作る
この記事では、MQL5のエキスパートアドバイザー(EA)を使用した自動化について詳しく説明し、初期のバックテスト結果を分析します。この戦略には高い保有能力が必要であることを強調し、今後の回で距離、takeProfit、ロットサイズなどの主要パラメータを最適化する計画を概説します。本連載は、取引戦略の効率性と異なる市場環境への適応性を高めることを目的としています。
PythonとMetaTrader5 Pythonパッケージを使用した深層学習による予測と注文とONNXモデルファイル PythonとMetaTrader5 Pythonパッケージを使用した深層学習による予測と注文とONNXモデルファイル
このプロジェクトでは、金融市場における深層学習に基づく予測にPythonを使用します。平均絶対誤差(MAE)、平均二乗誤差(MSE)、R二乗(R2)などの主要なメトリクスを使用してモデルのパフォーマンスをテストする複雑さを探求し、すべてを実行ファイルにまとめる方法を学びます。また、そのEAでONNXモデルファイルを作成します。