English Русский 中文 Español Deutsch Português
MQL4による取引におけるファジー論理の適用

MQL4による取引におけるファジー論理の適用

MetaTrader 4 | 5 9月 2016, 10:53
1 737 0
Alexander Fedosov
Alexander Fedosov

概論

昨今の取引において、取引エキスパートアドバイザまたはロボットと呼ばれる自動売買システムが益々使用されています。自動売買システムの要点は、正確かつ厳格に指定されたシステム持ち、取引戦略と資産管理(マネーマネジメント)システムを備えている点にあります。これらのシステムの利点は、人的要因を排除し、特定のアルゴリズムに則り厳格に動作する点です。自動売買の欠点としては、適用する取引戦略の柔軟性を欠き、常に同じ範囲で、厳しく分類されたパラメータを使って動作する点があげられます。簡単に言えば、システムは平均的なトレンドでは、1ロットでエントリーし、強いトレンドでは2ロットでエントリーをするというだけです!

曖昧なカテゴリやエントリーシグナルが一致した時の異なる意見が機械とは違う考えをもつ人間が取引をしたらどうなるのだろうか?恐らく「トレンドはどれくらい中程度のものなのか?強いトレンド寄りか?もしくはトレンドは強いが、ストラテジーに沿って2ロットでポジションを持つほどではないのではないか?」という疑問を投げかけながらトレンドの評価をしようとするでしょう。これらは全てファジー論理で説明できます。これは分類間の厳しい境界線を引かず、ロボットの規律正しさと人間の思考の柔軟性を組み合わせて、取引システムをより柔軟なものにします。この記事ではMQL4による取引におけるファジー論理の適用例をご紹介します。


メンバーシップ関数の説明

ファジー論理の理論の概念を知る為に、ファジーロジックの概要の記事を読み、MQL4の為のFuzzyNetライブラリの動作基礎を学ばれることをお勧めします(これは例を実装する際に使用します)。

記事内で使用するメンバーシップ関数を見てみましょう。


三角形メンバーシップ関数

三角形の形をとるメンバーシップ関数です。これはシンプルで、かつ以下の分析式で最も頻繁に使用される関数です。

三角形関数の式

一般に、これは不確定なタイプを指定する為に使用されます(『ほぼ等しい』、『平均値』、『インターバル内』、『オブジェクトと同じ』、『オブジェクトに似ている』など)。三角形メンバーシップ関数のパラメータは、通常このように解釈されます。

  • [a, c] — 変数の変動範囲。
  • b — 変数の最も可能性のある値。

図1. 三角形メンバーシップ関数


台形メンバーシップ関数

台形の形をとるメンバーシップ関数です。以下の式で設定されます。

台形メンバーシップ関数

台形メンバーシップ関数のパラメータは次のように解釈されます。

  • [a, d] — ファジー集合のキャリア、変数値の悲観的評価。
  • [b, c] — ファジー集合のコア、変数値の楽観的評価。

図2. 台形メンバーシップ関数


ベル型メンバーシップ関数

ベルの形をとる対照的曲線のメンバーシップ関数この関数は次の式で指定されます。

ベル型メンバーシップ関数

この関数のパラメータ値は、このように解釈されます。

  • a — メンバーシップ関数の濃度比。
  • b — メンバーシップ関数の傾斜比。
  • c — メンバーシップ関数の最大値の座標。

図3. ベル型メンバーシップ関数


シグモイドメンバーシップ関数

以下の式で指定され、単調なメンバーシップ関数を指定する為に使用されます。

シグモイドメンバーシップ関数

このパラメータは次のように解釈されます。

  • a — メンバーシップ関数の傾斜比。
  • с — メンバーシップ関数の変曲座標。

図4. シグモイドメンバーシップ関数


MQL4の為のFuzzyNetライブラリによるインディケータの実装例

例として、私は平均方向性指数(Average Directional Movement IndexまたはADX)を使用することにしました。これは、現在のトレンドの強さ(緑の太線)を表すトレンド指標です。まず初めに、トレンドの強さの正確なカテゴリを紹介します(図5)。
  • Weak Trend — 30から50の範囲にある緑の主要線の値。この厳しく決められた範囲内にある指標を、私達は弱いトレンドとして分類します。
  • Average Trend — 50から70の範囲にある緑の主要線の値。この範囲の指標を、私達は軽度または中程度のトレンドとして分類します。
  • Strong Trend — 70以上から100の最大値までの範囲にある緑の主要線の値。これは強いトレンドのサインとなります。

図5. 動作例とトレンドの強さごとのグラデーション

この厳格に決められた3つのカテゴリには、その明確かつ厳格な分類論理によって引き起こされるいくつかの欠点があります。

  • 第一の欠点は、主観的な点です。なぜ境界値を30、50、70とするのでしょうか?これは25、50、70の方がいいかもしれないし、もしくは他の値の方がいいかもしれないのではないでしょうか?これは自分の境界線を勧める他のトレーダーと同様に、幾らか正しいもので、少なくともこれらの様々な意見の結果が、ADXをトレードに適用した時に正反対の結果をもたらすことにつながります。
  • 第二の問題は、選択したカテゴリの境界範囲にあります。例えば、50という値は弱いトレンドと中程度のトレンドの境界線です。48や49という値は弱いトレンドで、50や51といった値は中程度となります。問題は49から50への移行の定義にあります。数値間の違いは、48と49の間と同じように1なのに、ここでは急に他のカテゴリへの移ってしまいます。

これらの欠点を改善する為に、ファジー論理は何を推奨するのでしょうか?

次のことが起こります。指定した境界線がぼやかされ、不確定なものとなります。つまり、厳しく指定したカテゴリの境界周辺の領域に二面性が出現します。トレンドの強さは異なる属性の段階を持ち二つの概念に関係します。これは、現在のトレンドの強さは、小さい段階(30%)では弱いトレンド、大きい段階(70%)は中程度のトレンドに似ているということができます。人はきっとこれを、トレンドは弱気トレンドよりも中程度のものだろうと思うでしょう。私は始めから厳しく決められたパラメータの評価の柔軟性こそが、ファジー論理の主要な利点があると考えています。ADXを用いた私達の例の為に、私は以下の上記で紹介したメンバーシップ関数を選択しました。

  • 台形関数は、弱いトレンドの表示に使われます。
  • ベル型関数は、平均/中程度のトレンドの表示に使われます。
  • シグモイド関数は、強いトレンドの為に使われます。

多数のカテゴリをもつより複雑なシステムは、FuzzyNetライブラリで利用できる他の関数で表示することができます。現段階では、そういった関数は数十種類あります。では、私達の例をグラフに示します。


図6. ファジー論理を使用したトレンドの強さの表示

チャート上に同時に二つのカテゴリには入る領域があるのが分かります。50-60の領域では、これは同時に弱気かつ中程度のトレンドで、60-70の領域では、これは中程度かつ強いトレンドになっています。三つのカテゴリの為に、私達は所定のメンバーシップ関数で設定された用語を定義しました。メンバーシップ関数で記述されたADXの入力値で、私達は入力値や非ファジー化の結果に何が起こるかを定義し、ファジー推論のアルゴリズムを選択する必要があります。

私達の例では、入力値として私は、トレンドの強さのファジー変数に関係する証拠金リスク率の値を選択しました。つまり、トレンドが強ければ強いほど、リスクも取引に使用する証拠金率も高くなります。推論アルゴリズムとして、マムダニを選びました。

また、トレンドの強さに対するものと同じように、三つのリスクの度合いのカテゴリを導入します。

  • 低リスク (Low) — 証拠金の2から4%の範囲。
  • 通常のリスク (Normal) — 4-5%。
  • 高リスク(High) — 5から証拠金の10%の最大値まで。

トレンドの強さと同様に、リスクの度合いをメンバーシップ関数を使って記述します。

  • 台形関数は、低リスク用です。
  • 三角形関数は、通常リスク用です。
  • シグモイド関数は、高リスク用です。

ファジー論理を使用したチャートは次のようになりました。


図7. ファジー論理によるリスクの度合いの表示


MQL4の為のFuzzyNetライブラリを使って、記述したデータを実装します。

//+------------------------------------------------------------------+
//|                                                    ADX_Fuzzy.mq4 |
//|                                                Alexander Fedosov |
//|                           https://www.mql5.com/ru/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "Alexander Fedosov"
#property link      "https://www.mql5.com/ru/users/alex2356"
#property version   "1.00"
#property strict
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_color1 Green
#property indicator_color2 Red
#property indicator_minimum 0
#property indicator_maximum 10
//+------------------------------------------------------------------+
//| Connecting libraries                                             |
//+------------------------------------------------------------------+
#include <Math\FuzzyNet\MamdaniFuzzySystem.mqh>
//--- input parameters
input string  p1="==== Parameters ====";
input int    visual=100;             // Visual Period
input int    adx_period=10;          // ADX Period
//---
double Buffer1[],Buffer2[],adx,adx_di_minus,adx_di_plus;
int limit;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexStyle(0,DRAW_HISTOGRAM,0,2);
   SetIndexBuffer(0,Buffer1);
   SetIndexEmptyValue(0,0.0);
//---
   SetIndexStyle(1,DRAW_HISTOGRAM,0,2);
   SetIndexBuffer(1,Buffer2);
   SetIndexEmptyValue(1,0.0);
//---
   ArrayResize(Buffer1,visual);
   ArrayResize(Buffer2,visual);
   ArrayInitialize(Buffer1,0.0);
   ArrayInitialize(Buffer2,0.0);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {
   int count_bars=IndicatorCounted();
//---
   if(count_bars<0)
      return(-1);
//---
   if(Bars-1<adx_period)
      return(0);
//---
   for(int i=0; i<visual;i++)
     {
      adx=NormalizeDouble(iADX(_Symbol,PERIOD_CURRENT,adx_period,PRICE_CLOSE,MODE_MAIN,i),_Digits);
      adx_di_plus=NormalizeDouble(iADX(_Symbol,PERIOD_CURRENT,adx_period,PRICE_CLOSE,MODE_PLUSDI,i),_Digits);
      adx_di_minus=NormalizeDouble(iADX(_Symbol,PERIOD_CURRENT,adx_period,PRICE_CLOSE,MODE_MINUSDI,i),_Digits);
      //---
      double r=(adx_di_plus-adx_di_minus);
      if(MathAbs(r)>10 && adx>=30.0)
         if(r>0)
            Buffer1[i]=mamdani(adx);
      else if(r<0)
         Buffer2[i]=mamdani(adx);
     }
   return(rates_total);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double mamdani(double v)
  {
   double res=0;
//--- Mamdani Fuzzy System  
   MamdaniFuzzySystem *fsRisk=new MamdaniFuzzySystem();
//--- Create input variables for the system
   FuzzyVariable *fsTrend=new FuzzyVariable("trend",30.0,100.0);
//---
   fsTrend.Terms().Add(new FuzzyTerm("weak", new TrapezoidMembershipFunction(20.0, 30.0, 50.0, 60.0)));
   fsTrend.Terms().Add(new FuzzyTerm("average", new GeneralizedBellShapedMembershipFunction(2.5,2.0,60.0)));
   fsTrend.Terms().Add(new FuzzyTerm("strong",new SigmoidalMembershipFunction(0.4,75.0)));
   fsRisk.Input().Add(fsTrend);
//--- Create Output
   FuzzyVariable *fvRisk=new FuzzyVariable("risk",2.0,10.0);
   fvRisk.Terms().Add(new FuzzyTerm("low", new TrapezoidMembershipFunction(1.0, 2.0, 3.0, 4.0)));
   fvRisk.Terms().Add(new FuzzyTerm("normal", new TriangularMembershipFunction(3.0, 4.0, 5.0)));
   fvRisk.Terms().Add(new FuzzyTerm("high", new SigmoidalMembershipFunction(6.0,5.0)));
   fsRisk.Output().Add(fvRisk);
//--- Create three Mamdani fuzzy rules
   MamdaniFuzzyRule *rule1 = fsRisk.ParseRule("if (trend is weak) then risk is low");
   MamdaniFuzzyRule *rule2 = fsRisk.ParseRule("if (trend is average) then risk is normal");
   MamdaniFuzzyRule *rule3 = fsRisk.ParseRule("if (trend is strong) then risk is high");
//--- Add three Mamdani fuzzy rules in the system
   fsRisk.Rules().Add(rule1);
   fsRisk.Rules().Add(rule2);
   fsRisk.Rules().Add(rule3);
//--- Set input value
   CList *in=new CList;
   Dictionary_Obj_Double *p_od_in=new Dictionary_Obj_Double;
   p_od_in.SetAll(fsTrend,v);
   in.Add(p_od_in);
//--- Get result
   CList *result;
   Dictionary_Obj_Double *p_od_out;
   result=fsRisk.Calculate(in);
   p_od_out=result.GetNodeAtIndex(0);
   res=NormalizeDouble(p_od_out.Value(),_Digits);
//---
   delete in;
   delete result;
   delete fsRisk;
   return res;
  }
//+------------------------------------------------------------------+

トレンドの強さからのリスクの度合いに応じた私達のシステムの可視的実装は、棒グラフとしての簡単なインディケータにし(強いトレンドの時は緑色、弱いトレンドの時は赤色)、その高さは上述した範囲内のトレンドの強さの時のリスクの度合いの数値を示しています。コードをより詳細に見てみましょう。

初めに、適用するヒストグラムの為の二つのバッファと、ゼロから最大の所定のリスク値(10%)までの縦座標の軸による色と範囲を設定します。

#property indicator_separate_window
#property indicator_buffers 2
#property indicator_color1 Green
#property indicator_color2 Red
#property indicator_minimum 0
#property indicator_maximum 10

次に、マムダニアルゴリズムに基づくシステム作成の為のライブラリを接続し、またゼロから始まるバーの数とADXインディケータの期間の設定値を可視化する為の変数を追加します。

//+------------------------------------------------------------------+
//| Connecting libraries FuzzyNet                                    |
//+------------------------------------------------------------------+
#include <Math\FuzzyNet\MamdaniFuzzySystem.mqh>
//--- input parameters
input string  p1="==== Parameters ====";
input int    visual=100;             // Visual Period
input int    adx_period=10;          // ADX Period

初期化時に、私達のインディケータが棒グラフの形になるように設定します。

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexStyle(0,DRAW_HISTOGRAM,0,2);
   SetIndexBuffer(0,Buffer1);
   SetIndexEmptyValue(0,0.0);
//---
   SetIndexStyle(1,DRAW_HISTOGRAM,0,2);
   SetIndexBuffer(1,Buffer2);
   SetIndexEmptyValue(1,0.0);
//---
   ArrayResize(Buffer1,visual);
   ArrayResize(Buffer2,visual);
   ArrayInitialize(Buffer1,0.0);
   ArrayInitialize(Buffer2,0.0);
//---
   return(INIT_SUCCEEDED);
  }

メインコードではADXインディケータの主な表示を定義し、r変数では+DIとDIの二つのインディケータの間の差を見つけます。10以上の+DIと-DIの絶対値と30以上のトレンドの強さの主要値(弱いトレンドの下限値)の差としてトレンドフィルターを導入します。次にr変数の符号に基づいてトレンドの方向性を定義し、以前に指定した配列へmamdani()関数の値を入れます。

int count_bars=IndicatorCounted();
//---
   if(count_bars<0)
      return(-1);
//---
   if(Bars-1<adx_period)
      return(0);
//---
   for(int i=0; i<visual;i++)
     {
      adx=NormalizeDouble(iADX(_Symbol,PERIOD_CURRENT,adx_period,PRICE_CLOSE,MODE_MAIN,i),_Digits);
      adx_di_plus=NormalizeDouble(iADX(_Symbol,PERIOD_CURRENT,adx_period,PRICE_CLOSE,MODE_PLUSDI,i),_Digits);
      adx_di_minus=NormalizeDouble(iADX(_Symbol,PERIOD_CURRENT,adx_period,PRICE_CLOSE,MODE_MINUSDI,i),_Digits);
      //---
      double r=(adx_di_plus-adx_di_minus);
      if(MathAbs(r)>10 && adx>=30.0)
         if(r>0)
            Buffer1[i]=mamdani(adx);
      else if(r<0)
         Buffer2[i]=mamdani(adx);
     }

mamdani関数の説明:

1. *fsRiskのマムダニタイプのファジー論理の新しいシステムを作成します。

2. そこに設定したtrend名と30と100の最小/最大値を持つ*fsTrend入力変数を追加します。

//--- Mamdani Fuzzy System  
   MamdaniFuzzySystem *fsRisk=new MamdaniFuzzySystem();
//--- Create input variables for the system
   FuzzyVariable *fsTrend=new FuzzyVariable("trend",30.0,100.0);

3. 次に上述(図6)の各カテゴリに対して選択したメンバーシップ関数を持つファジー論理を追加します。

fsTrend.Terms().Add(new FuzzyTerm("weak", new TrapezoidMembershipFunction(30.0, 40.0, 50.0, 60.0)));
   fsTrend.Terms().Add(new FuzzyTerm("average", new GeneralizedBellShapedMembershipFunction(2.5,2.0,60.0)));
   fsTrend.Terms().Add(new FuzzyTerm("strong",new SigmoidalMembershipFunction(0.4,75.0)));
   fsRisk.Input().Add(fsTrend);

4. 入力値に対して2-3の項目を渡します。risk名と2%と10%の最小/最大リスク値を持つ*fvRisk変数を作成します。

//--- Create Output
   FuzzyVariable *fvRisk=new FuzzyVariable("risk",2.0,10.0);
   fvRisk.Terms().Add(new FuzzyTerm("low", new TriangularMembershipFunction(2.0, 3.0, 4.0)));
   fvRisk.Terms().Add(new FuzzyTerm("normal", new TriangularMembershipFunction(3.0, 4.0, 5.0)));
   fvRisk.Terms().Add(new FuzzyTerm("high", new SigmoidalMembershipFunction(6.0,5.0)));
   fsRisk.Output().Add(fvRisk);

5. 私達のシステム、つまり3つのルールを表すファジールールのグループを作成します。

  • 弱いトレンドの場合、低リスク。
  • 強いトレンドの場合、通常のリスク。
  • 強いトレンドの場合、高リスク。
//--- Create three Mamdani fuzzy rules
   MamdaniFuzzyRule *rule1 = fsRisk.ParseRule("if (trend is weak) then risk is low");
   MamdaniFuzzyRule *rule2 = fsRisk.ParseRule("if (trend is average) then risk is normal");
   MamdaniFuzzyRule *rule3 = fsRisk.ParseRule("if (trend is strong) then risk is high");

6. システムへ私達のルールを追加します。

//--- Add three Mamdani fuzzy rules in the system
   fsRisk.Rules().Add(rule1);
   fsRisk.Rules().Add(rule2);
   fsRisk.Rules().Add(rule3);

7. 入力/出力変数の為のリストを作成し、mamdani関数の引数であるv入力パラメータを追加します。つまり、mamdani関数の全ての為に設定した入力/出力ファジー変数を持つファジー論理のシステムを作成し、入力にはADXインディケータの値が使用されます。

//--- Set input value
   CList *in=new CList;
   Dictionary_Obj_Double *p_od_in=new Dictionary_Obj_Double;
   p_od_in.SetAll(fsTrend,v);
   in.Add(p_od_in);
//--- Get result
   CList *result=new CList;
   Dictionary_Obj_Double *p_od_out=new Dictionary_Obj_Double;
   result=fsRisk.Calculate(in);
   p_od_out=result.GetNodeAtIndex(0);
   res=NormalizeDouble(p_od_out.Value(),_Digits);

8. res変数は関数値と結果で、それに基づき棒グラフが作成されます。

adx=NormalizeDouble(iADX(_Symbol,PERIOD_CURRENT,adx_period,PRICE_CLOSE,MODE_MAIN,i),_Digits);
      adx_di_plus=NormalizeDouble(iADX(_Symbol,PERIOD_CURRENT,adx_period,PRICE_CLOSE,MODE_PLUSDI,i),_Digits);
      adx_di_minus=NormalizeDouble(iADX(_Symbol,PERIOD_CURRENT,adx_period,PRICE_CLOSE,MODE_MINUSDI,i),_Digits);
      //---
      double r=(adx_di_plus-adx_di_minus);
      if(MathAbs(r)>10 && adx>=30.0)
         if(r>0)
            Buffer1[i]=mamdani(adx);
      else if(r<0)
         Buffer2[i]=mamdani(adx);

その結果、私達はインディケータの動作の次の視覚的結果を観察することができます。

図8. インディケータの動作

この動作から分かるように、トレンドの存在時にカラー棒グラフとしてのインディケータはこれを示し、高さは証拠金の推奨リスク率を示しています。ここで明白な疑問が生じます。同じインディケータがいつも通りに明確な間隔を持って実装された場合、どのような差が生じるのでしょうか?この為に次の項目をより詳細に見ていきましょう9を参照)。ここでは緑色の矢印で棒グラフが示され、左にはその数値とADXのトレンドの強さの数値が示されています。前に私達が定義したように、70以上のADX値は強いトレンドであり、強いトレンドの時には5%以上のリスク値となります。しかし図9で良くわかるように、ADX = 69.7923で、つまり厳しい条件下ではまだ中程度のトレンドであり、リスクは5%以下であるべきなのに、ここでは5,6406で、つまりそれ以上です。


図9. 標準とファジー論理の違いの実証

これこそ動作の中のファジー論理であり、これは数値は70以下であるが、この領域のトレンドは中程度よりも強いトレンドに近いものであると判断しました。図6を見ることで、横座標の値が69,7923の時、強いトレンドのメンバーシップ関数は中程度のトレンドの関数よりも高い値を持っているということが分かります。したがって、私達のシステムは、明確な論理を持つシステムが行うよりも柔軟である、強いトレンドと中程度のトレンドのカテゴリの境界線上の評価を取りつつ、5%以上のリスクを与えました。


MQL4の為のFuzzyNetライブラリを使用した取引エキスパートアドバイザの実装例

エキスパートアドバイザの実装例では、明確に定義された条件下の場合とファジー論理の要素を使用した場合の動作の差を示したいと思います。今後の比較を完全なものにするために、私の他の記事( 価格変動の速度と傾向に基づくトレードアイディア)からエキスパートアドバイザの例を引用することにしました。こちらでは、トレードロボットの動作の要点が十分に詳細に説明されていて、ここで繰り返す必要がないので、これをベースにしつつ次の変更を加えます

  • 。このエキスパートアドバイザの動作論理は、価格変動の持続性のアイディアに基づいています。変動パラメータは、RSI(速度指標)とAC(加速指標)のインディケータによって表示されます。速度と加速の評価は、これらのインディケータの値の間隔の索引づけを使用して行われます。ここで、RSI指標の計算と表示にファジー集合の理論を適用します。私達のシステムの入力にはRSI値が、出力では1-4の整数以外(1.3や3.85など)の値も持つ速度のファジーインデックスを使用します。
  • 次に、ファジーインデックスの値は、他のシステムの入力として使われ、出力には利益の値を使用します。つまり、元のエキスパートアドバイザの指標は全く変更のないまま、テイクプロフィットとなります。

この背景にある考え方は至って単純なものです。RSIとACが動きのパラメータである場合、速度が速ければ速いほど、この動きの持続性は高くなり、つまりより大きなテイクプロフィットを配置するのが妥当です。したがって、低い速度の場合、つまり穏やかな動きの時は、ロールバックまたはトレンドの反転をさせないために、利益目標はより緊密に設定する必要があります。図10ではこのトレードエキスパートアドバイザのファジー論理の適用をより明確にする為の図を示しています。


図10. エキスパートアドバイザのファジー論理の適用図

インディケータを使用した例でも、両方のファジーモデルの為のメンバーシップ関数を記述します。一つ目は、RSI指標のファジーモデルで、入力にはインディケータの数値が入る為、私達に必要な値を三つのカテゴリに分割します。

  • Weak. 最初のカテゴリは弱いトレンドを定義しています。RSI値: 60-70.
  • Average. 二つ目のカテゴリは中程度のトレンドを定義しています。RSI値: 70-80.
  • Strong. 三つ目のカテゴリは強いトレンドを定義しています。値は80-85となります。

指定したカテゴリを表示する為のメンバーシップ関数を選択します。

  • Weak. -0.75の傾き比と67.5の変曲点を持つシグモイド関数。
  • Average. 72.5の最大座標と2.2の濃度比を持つガウス関数。
  • Strong. 80の最大座標と1.4の濃度比を持つガウス関数。

可視的なプレゼンテーションは次の通りです。


図11. RSI値カテゴリのメンバーシップ関数の説明

このファジーモデルの出力パラメータはRSI指標です。これを説明する為に、次のカテゴリとメンバーシップ関数を使用します。

  • Low。1-2の範囲内の低い指数。メンバーシップ関数は、11の傾きと1.5の変曲点を持つシグモイド関数。
  • Normal。2-3の範囲内の中程度の指数。メンバーシップ関数は、最大値が2で0.3の濃度比を持つガウス関数。
  • High。3-4の範囲内の高い指数。メンバーシップ関数は、6の傾きと3の変曲点を持つシグモイド関数。

結果は次のようになります。


図12. RSI指標値カテゴリのメンバーシップ関数の説明

次に図10から二つ目のファジーモデル(テイクプロフィット計算のファジーモデル)を説明します。このモデルの入力パラメータは、すでに一つ目のモデルの出力パラメータ(RSIファジー指数)として記述しました。出力パラメータはテイクプロフィットの値となります。明確なカテゴリの為に次のように定義します。

  • Minimal。テイクプロフィットの最小カテゴリ。30-40の範囲内。
  • Average。テイクプロフィットの平均カテゴリ。40-60の範囲内。
  • Maximal。テイクプロフィットの高カテゴリ。60-70の範囲内。

それでは、メンバーシップ関数を記述します。

  • Minimal。メンバーシップ関数は、0.8の傾きと37.5の変曲点を持つシグモイド関数。
  • Average。 メンバーシップ関数は、50の最大座標と3の濃度比を持つガウス関数。
  • Maximal。メンバーシップ関数は、0.8の傾きと62.5の変曲点を持つシグモイド関数。

すると、グラフィックの実装は以下のようになります。


図13. テイクプロフィット値カテゴリのメンバーシップ関数による表示

これで全てのパラメータが定義されたので、あとはこれをRSIインディケータの指数を元に、ストップロスとテイクプロフィットの値を連続計算する二つのファジーモデルを追加するトレードエキスパートアドバイザで実装します。

//+------------------------------------------------------------------+
//|                                                       tester.mq4 |
//|                                                Alexander Fedosov |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Alexander Fedosov"
#property strict
#include "trading.mqh" //取引操作の為の補助ライブラリ
//+------------------------------------------------------------------+
//| Connecting libraries                                             |
//+------------------------------------------------------------------+
#include <Math\FuzzyNet\MamdaniFuzzySystem.mqh>
//+------------------------------------------------------------------+
//| エキスパートアドバイザのパラメータ                                              |
//+------------------------------------------------------------------+
input bool           Lot_perm=false;               // 証拠金からのロットは?
input double         Risk = 2;                     // 証拠金リスク、%
input double         lt=0.01;                      // ロット
input int            magic=2356;                   // マジック
input int            period=14;                    // RSIインディケータの期間
input ENUM_TIMEFRAMES tf=PERIOD_CURRENT;           // 動作時間軸
//---
int index_rsi,index_ac;
double tkp,stl;
double rs,mdm;
CTrading tr;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   tr.Trading(magic,5,lt,Lot_perm,Risk);
   tr.exp_name="Tester Fuzzy Logic";
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| 主要な演算機能                                          |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- 保有している注文が無いかをチェック
   if(!tr.isOpened(magic))
     {
      depth_trend();
      speed_ac();
      rs=tr.ND(iRSI(_Symbol,tf,period,PRICE_CLOSE,0),2);
      //--- 買い条件のチェック
      if(Buy() && rs<=85.0)
        {
         mdm = mamdani_rsi(rs);
         tkp = MathCeil(mamdani_tp(mdm));
         stl = MathCeil(tkp*0.43);
         if(tr.OpnOrd(OP_BUY,lt,(int)tkp,(int)stl))
            Print("RSIは等しい",rs," TPは等しい",tkp," SLは等しい",stl);
        }
      //--- 売り条件のチェック
      if(Sell() && rs>=15.0)
        {
         mdm = mamdani_rsi(100-rs);
         tkp = MathCeil(mamdani_tp(mdm));
         stl = MathCeil(tkp*0.43);
         if(tr.OpnOrd(OP_SELL,lt,(int)tkp,(int)stl))
            Print("RSIは等しい",rs," TPは等しい",tkp," SLは等しい",stl);
        }
     }
//--- 開いている注文があるか?
   if(tr.isOpened(magic))
     {
      //--- 決済の条件を満たす売り注文を確認し決済する
      if(Sell_close())
         tr.ClosePosAll(OP_SELL);
      //--- 決済の条件を満たす買い注文を確認し決済する
      if(Buy_close())
         tr.ClosePosAll(OP_BUY);
     }
  }
//+------------------------------------------------------------------+
//| トレンドの深さを判定する機能                               |
//+------------------------------------------------------------------+
void depth_trend()
  {
//--- 市場エントリのインデックスの定義
   double rsi=iRSI(_Symbol,tf,period,PRICE_CLOSE,0);
//---
   index_rsi=0;
   if(rsi>90.0)
      index_rsi=4;
   else if(rsi>80.0)
      index_rsi=3;
   else if(rsi>70.0)
      index_rsi=2;
   else if(rsi>60.0)
      index_rsi=1;
   else if(rsi<10.0)
      index_rsi=-4;
   else if(rsi<20.0)
      index_rsi=-3;
   else if(rsi<30.0)
      index_rsi=-2;
   else if(rsi<40.0)
      index_rsi=-1;
  }
//+------------------------------------------------------------------+
//| トレンドの速度を判定する機能                              |
//+------------------------------------------------------------------+
void speed_ac()
  {
   double ac[];
   ArrayResize(ac,5);
   ArrayInitialize(ac,0.0);
   for(int i=0; i<5; i++)
      ac[i]=iAC(_Symbol,tf,i);
//---
   index_ac=0;
//--- 買いインデックス
   if(ac[0]>ac[1])
      index_ac=1;
   else if(ac[0]>ac[1] && ac[1]>ac[2])
      index_ac=2;
   else if(ac[0]>ac[1] && ac[1]>ac[2] && ac[2]>ac[3])
      index_ac=3;
   else if(ac[0]>ac[1] && ac[1]>ac[2] && ac[2]>ac[3] && ac[3]>ac[4])
      index_ac=4;
//--- 売りインデックス
   else if(ac[0]<ac[1])
      index_ac=-1;
   else if(ac[0]<ac[1] && ac[1]<ac[2])
      index_ac=-2;
   else if(ac[0]<ac[1] && ac[1]<ac[2] && ac[2]<ac[3])
      index_ac=-3;
   else if(ac[0]<ac[1] && ac[1]<ac[2] && ac[2]<ac[3] && ac[3]<ac[4])
      index_ac=-4;
  }
//+------------------------------------------------------------------+
//| 買い条件のチェック機能                              |
//+------------------------------------------------------------------+
bool Buy()
  {
   return (((index_rsi==2 && index_ac>=1) || (index_rsi==3 && index_ac==1))?true:false);
  }
//+------------------------------------------------------------------+
//| 売り条件のチェック機能                              |
//+------------------------------------------------------------------+
bool Sell()
  {
   return (((index_rsi==-2 && index_ac<=-1) || (index_rsi==-3 && index_ac==-1))?true:false);
  }
//+------------------------------------------------------------------+
//| ロングポジションを閉じる条件をチェックする機能             |
//+------------------------------------------------------------------+
bool Buy_close()
  {
   return ((index_rsi>2 && index_ac<0)?true:false);
  }
//+------------------------------------------------------------------+
//| ショートポジションを閉じる条件をチェックする機能             |
//+------------------------------------------------------------------+
bool Sell_close()
  {
   return ((index_rsi<-2 && index_ac>0)?true:false);
  }
//+------------------------------------------------------------------+
//| RSIインデックスを計算するファジーモデル関数                   |
//+------------------------------------------------------------------+
double mamdani_rsi(double rsi)
  {
   double res=0;
//--- Mamdani Fuzzy System  
   MamdaniFuzzySystem *fsRSI=new MamdaniFuzzySystem();
//--- システムと用語の為の入力変数の作成
   FuzzyVariable *fsTrend=new FuzzyVariable("rsi",60.0,85.0);
//---
   fsTrend.Terms().Add(new FuzzyTerm("weak", new SigmoidalMembershipFunction(-0.75,67.5)));
   fsTrend.Terms().Add(new FuzzyTerm("average", new NormalMembershipFunction(72.5,2.2)));
   fsTrend.Terms().Add(new FuzzyTerm("strong", new NormalMembershipFunction(80.0,1.4)));
   fsRSI.Input().Add(fsTrend);
//--- システムの為の出力変数の作成と用語の定義
   FuzzyVariable *fsIndex=new FuzzyVariable("index",1.0,4.0);
   fsIndex.Terms().Add(new FuzzyTerm("low", new SigmoidalMembershipFunction(-11.0,1.5)));
   fsIndex.Terms().Add(new FuzzyTerm("normal", new NormalMembershipFunction(2.0,0.3)));
   fsIndex.Terms().Add(new FuzzyTerm("high", new SigmoidalMembershipFunction(6.0,3.0)));
   fsRSI.Output().Add(fsIndex);
//--- ファジールールの作成とシステムへのそれらの追加
   MamdaniFuzzyRule *rule1 = fsRSI.ParseRule("if (rsi is weak) then (index is low)");
   MamdaniFuzzyRule *rule2 = fsRSI.ParseRule("if (rsi is average) then (index is normal)");
   MamdaniFuzzyRule *rule3 = fsRSI.ParseRule("if (rsi is strong) then (index is high)");
   fsRSI.Rules().Add(rule1);
   fsRSI.Rules().Add(rule2);
   fsRSI.Rules().Add(rule3);
//--- 入力値の設定
   CList *in=new CList;
   Dictionary_Obj_Double *p_od_in=new Dictionary_Obj_Double;
   p_od_in.SetAll(fsTrend,rsi);
   in.Add(p_od_in);
//--- 結果の出力
   CList *result=new CList;
   Dictionary_Obj_Double *p_od_out=new Dictionary_Obj_Double;
   result=fsRSI.Calculate(in);
   p_od_out=result.GetNodeAtIndex(0);
   res=NormalizeDouble(p_od_out.Value(),_Digits);
//---
   delete in;
   delete result;
   delete fsRSI;
   return res;
  }
//+------------------------------------------------------------------+
//| テイクプロフィットのインデックスを計算するファジーモデル関数          |
//+------------------------------------------------------------------+
double mamdani_tp(double ind_rsi)
  {
   double res=0;
//--- Mamdani Fuzzy System  
   MamdaniFuzzySystem *fsTP=new MamdaniFuzzySystem();
//--- システムと用語の為の入力変数の作成
   FuzzyVariable *fsIndex=new FuzzyVariable("index",1.0,4.0);
   fsIndex.Terms().Add(new FuzzyTerm("low", new SigmoidalMembershipFunction(-11.0,1.5)));
   fsIndex.Terms().Add(new FuzzyTerm("normal", new NormalMembershipFunction(2.0,0.3)));
   fsIndex.Terms().Add(new FuzzyTerm("high", new SigmoidalMembershipFunction(6.0,3.0)));
   fsTP.Input().Add(fsIndex);
//--- システムの為の出力変数の作成と用語の定義
   FuzzyVariable *fsProfit=new FuzzyVariable("TP",30.0,70.0);
   fsProfit.Terms().Add(new FuzzyTerm("minimal", new SigmoidalMembershipFunction(-0.8,37.5)));
   fsProfit.Terms().Add(new FuzzyTerm("average", new NormalMembershipFunction(50.0,3.0)));
   fsProfit.Terms().Add(new FuzzyTerm("maximal", new SigmoidalMembershipFunction(0.8,62.5)));
   fsTP.Output().Add(fsProfit);
//--- ファジールールの作成とシステムへのそれらの追加
   MamdaniFuzzyRule *rule1 = fsTP.ParseRule("if (index is low) then (TP is minimal)");
   MamdaniFuzzyRule *rule2 = fsTP.ParseRule("if (index is normal) then (TP is average)");
   MamdaniFuzzyRule *rule3 = fsTP.ParseRule("if (index is high) then (TP is maximal)");
   fsTP.Rules().Add(rule1);
   fsTP.Rules().Add(rule2);
   fsTP.Rules().Add(rule3);
//--- 入力値の設定
   CList *in=new CList;
   Dictionary_Obj_Double *p_od_in=new Dictionary_Obj_Double;
   p_od_in.SetAll(fsIndex,ind_rsi);
   in.Add(p_od_in);
//--- 結果の出力
   CList *result=new CList;
   Dictionary_Obj_Double *p_od_out=new Dictionary_Obj_Double;
   result=fsTP.Calculate(in);
   p_od_out=result.GetNodeAtIndex(0);
   res=NormalizeDouble(p_od_out.Value(),_Digits);
//---
   delete in;
   delete result;
   delete fsTP;
   return res;
  }
//+------------------------------------------------------------------+

ここで、エキスパートアドバイザに加えられた主な変更点を見ていきましょう。

  • 主な追加点は、まずmamdani_rsimamdani_tp関数としての二つのファジーモデルの実装です。
  • ストップロスやテイクプロフィットといったエキスパートアドバイザのパラメータが削除されました。これらは今後はファジー論理で計算されます。
  • では、この計算がどのように実装されるかを見てみましょう。
if(OrdersTotal()<1)
     {
      depth_trend();
      speed_ac();
      rs=tr.ND(iRSI(_Symbol,tf,period,PRICE_CLOSE,0),2);
      //--- 買い条件のチェック
      if(Buy() && rs<=85.0)
        {
         mdm = mamdani_rsi(rs);
         tkp = MathCeil(mamdani_tp(mdm));
         stl = MathCeil(tkp*0.43);
         if(tr.OpnOrd(OP_BUY,lt,tkp,stl))
            Print("RSIは等しい",rs," TPは等しい",tkp," SLは等しい",stl);
        }
      //--- 売り条件のチェック
      if(Sell() && rs>=15.0)
        {
         mdm = mamdani_rsi(100-rs);
         tkp = MathCeil(mamdani_tp(mdm));
         stl = MathCeil(tkp*0.43);
         if(tr.OpnOrd(OP_SELL,lt,tkp,stl))
            Print("RSIは等しい",rs," TPは等しい",tkp," SLは等しい",stl);
        }
     }

エキスパートアドバイザのマジックを持つ注文を保有していない場合、システムはdepth_trend()関数とspeed_ac()関数を使用し、市場の動きを観測し、Buy()またはSell()と一致した時にエントリします。次に、mdm変数の条件を満たす時に、ファジーモデルの動作結果の値が割り当てられ、その入力には現在のRSI値が、出力にはファジーインデックスが使用されます。その次に、ファジーインデックス値が私達の二つ目のシステムの入力に使用され、その出力にはポイントのテイクプロフィットが使用されます。テイクプロフィット値はtkp変数で割り当てられます。

0.43という比率は70ポイントの最大プロフィット値を元に計算し、そのストップロスは30ポイントとしました。発注に成功すると、私達のエキスパートアドバイザは、エキスパートアドバイザが動作しているRSI値を表示し、ストップロスとテイクプロフィットのパラメータ値からの計算値を表示します。これはテストをしやすくする為のものです。

次の点を明確にする必要があります。

  1. 売り条件の時には、mdm変数によってmamdani_rsi(100-rs)が割り当てられる。これはRSI値の最大値と最小値(0と100)に対して範囲とその境界線が鏡写しになっている為。
  2. 二つ目は、買いの時はrs<=85で売りの時はrs>=15という二つの追加条件です。これは、RSI指標算出のファジーモデルの入力変数を作成する際に、境界線が60-85に設定され、売りには15の極値となる為。

エキスパートアドバイザの動作例は図14でご参照ください。こちらでは、RSIインディケータの値の異なる値によって、ストップロスとテイクプロフィットの値の再計算が行われることが分かります。


図14. トレードエキスパートアドバイザの動作結果


まとめ

この記事では、MQL4のFuzzyNetライブラリを使った取引におけるファジー論理の実装例を検証しました。ファジー論理をベースにしたシステムが、トレンドの分類やリスクの分化のような明確なカテゴリの問題により柔軟に対応することが分かりました。トレードエキスパートアドバイザでは、ファジー論理のシステムが自分の取引戦略に基づきどのようにトレードシグナルの強さを分析するか、また損失の制限値と利益目標値を算出するかをデモンストレーションしました。ファジー論理をベースにしたトレードシステムは、トレードロボットの規則性と人間の試行の柔軟性といった、成功する取引に必要な最高の要素を組み合わせることができるものだと思います。

MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/2032

添付されたファイル |
trading.mqh (42.67 KB)
tester.mq4 (20.49 KB)
adx_fuzzy.mq4 (10.62 KB)
エキスパートアドバイザやインディケータ、スクリプトの入力パラメータを保存する為のテキストファイル エキスパートアドバイザやインディケータ、スクリプトの入力パラメータを保存する為のテキストファイル
この記事では、エキスパートアドバイザやインディケータ、スクリプトのテキストファイル内のプロパティとしての動的オブジェクトや配列、その他の変数の保存について説明します。MQL言語で提供される標準的なツールへの機能の追加に役立ちます。
トレーダーの為のライフハック:1つのバックテストは良いが、4つは更に良い トレーダーの為のライフハック:1つのバックテストは良いが、4つは更に良い
最初のテストをする時にトレーダーには「4つのモードのうちのどれを使ったらいいのだろうか?」という一つの疑問が浮かんでくると思います。モードにはそれぞれにそれぞれの利点と特徴があるので、それを簡単にして、ワンクリックで一度に全てのモードを起動させましょう!この記事では、Win APIとちょっとしたマジックを使って、4つのテストチャートを一度に表示する方法をご紹介します。
グラフィカルインタフェースVII: テーブルコントロール(チャプター 1) グラフィカルインタフェースVII: テーブルコントロール(チャプター 1)
MetaTraderグラフィカルインタフェースに関するシリーズの第七部では、テキストラベル、エディットボックスとレンダーボックスの3つのテーブルタイプについてお話します。後1つの重要かつ頻繁に使用されるコントロールはタブで、これは、他のコントロールのグループを表示/非表示してMQLアプリケーション内でスペースを有効に使ったインタフェースを開発することを可能にします。
領域法 領域法
取引システム『領域法』は、RSIオシレーターの通常ではない解釈において使われます。この記事では、領域法を可視化するインディケータと、このシステムに基づいてトレードを行うエキスパートアドバイザを提供します。記事では、様々な通貨ペアや時間軸、面積値でのエキスパートアドバイザの詳細なテスト結果が記述されています。