English Русский 中文 Español Deutsch Português
preview
MQL5の圏論(第10回):モノイド群

MQL5の圏論(第10回):モノイド群

MetaTrader 5 | 1 8月 2023, 09:46
369 0
Stephen Njuki
Stephen Njuki

はじめに

前回の記事では、可能な要素を拡張することによってモノイド集合を変換する手段として考えられたモノイド作用に取り組むことによって、モノイドについての考察を続けました。これまで全体として、始域、射、圏の公理、単射的引き戻しと全射的押し出しの概念をカバーしてきました。圏論の概念を実践するには、その概念のすべて/ほとんどをより幅広く研究する必要があるという意見もあるでしょうが、ここでは、基本的あるいは限定的な見方から、どのような考え方が役に立つかを探るというアプローチをとっています。場合によっては以前の記事から概念を借用したものもありますが、これらの記事ではそれぞれ単独で、トレーダーの意思決定を容易にする有効性を示し、場合によっては取引システムを定義する可能性を示しました。今回は、モノイドについて考えることにします。これらは、前回の記事で取り上げたモノイド作用と同様に、取引の決定時点におけるモノイドの再定義と見なされます。モノイド作用をモノイド集合の拡張とみなしたとを思い出してください。今回は、モノイドをもう一度再定義して、もうひとつのモノイドパラメータ、つまり単位元を再評価することになります。以前の記事では、MQL5ウィザードを使って完全なエキスパートアドバイザー(EA)を組み立てることを目標に、EAシグナルやEAEAトレーリングクラスのインスタンスをコーディングしましたが、この記事ではそれとは異なり、完全な取引システムを構築することはしません。むしろ、コード化されたモノイド、モノイド作用、モノイド群クラスの一部である個々の関数に注目し、それらがトレーダーにとって重要な意思決定ポイントでどのように役立つかを検証します。


モノイド群について

要約すると、モノイドは3つのもの、つまり集合、その集合に属する単位元、そしてその集合の任意の2つの要素を取り、常にその集合のメ要素を返す二項演算です。さらに、集合のいずれかの要素が二項演算で単位元と対になる場合、出力は常にその要素になります。最新の記事で取り上げたモノイド作用は、集合と二項演算によって定義される関数の一形態で、モノイド集合の要素とこの関数の集合を対にして、常に関数の集合の要素である要素を出力します。すべての出力は厳密にモノイド集合の要素であるため、モノイドの二項演算の出力は閉じられていることを思い出してください。

しかし、まず最初に、と、ここでモノイド群と呼んでいるものとの間には、それ自体何の区別もないことを指摘しておくのが建設的かもしれません。唯一の違いは、「モノイド」という接頭語に示唆されているように、ある圏に属する集合(あるいは始域)を厳密に指しているということです。したがって、の定義に従えば、モノイド群とは、モノイド集合のすべての要素は、その逆元である別の要素を持つべきであるという追加の性質を持つモノイドです。モノイドの二項演算で要素とその逆元が対になる場合、出力は常にモノイドの単位元になります。

形式的には、正則モノイドを除いたモノイド群を設定する逆元属性

は、すべての


に対して、逆元


があることになります。この逆は、


を満たします。ここでeはモノイドの単位元です。

トレーダーへの適用については、前回の記事と同様の方法で検討します。前回の記事では、モノイド(集合)をトレーダーが選択できる意思決定オプションのプールとして捉え、このプールのサイズをモノイド作用で拡張することについて考察しました。取引システムの決定ポイント(特徴)の相対的な重要度を重み付けする要因のリストに基づいて特定のモノイドの範囲を拡張した場合、これが取引システムのパフォーマンスにどのような影響を与えるかを調べました。その結果は、制限付き(デフォルト)モノイドを考慮したその前の記事で得られたものと比べると、平均から見て悪くなっていました。モノイド群については、モノイド集合の範囲を広げるのではなく、作用を持って群に変換された制限付きモノイドに戻します。このモノイド作用に伴う集合構成の変化については検討しますが、実際の取引システムの実装については今回は考慮しません。それは読者が独自に探求することです。


MQL5でモノイド群を実装する

モノイド群を実装するためのMQL5環境のセットアップでは、IDEを起動し、ウィザードから新しいスクリプトファイルを作成します。

script_create


次のタブでスクリプトの名前を「ct_10」とし、終了をクリックします。このスクリプトでは、前回の記事「ct_9.mqh」で紹介したクラスを修正したクラスファイル「ct_10.mqh」を参照します。完全を期すために、前の2つの記事で紹介した'ct_9.mqh'の一部であるモノイドクラスを作成する手順を順を追って説明することが役に立つかもしれません。このことは、これまではあまり注目されてきませんでしたが、今後は建設的なものになるはずです。基本的な構成単位は、主にデータ型Tのオブジェクトの配列を構成する要素クラスであることを思い出してください。データ型Tは要素の初期化時に設定されます。

//+------------------------------------------------------------------+
//| ELEMENT CLASS                                                    |
//+------------------------------------------------------------------+
template <typename T>
class CElement                      : public CObject
   {
      protected:
      
      int                           cardinal;
      T                             element[];
      
      public:
      
      bool                          Cardinality(int Value) { ... }
      int                           Cardinality() { return(cardinal); }
      
      ...
                                    
                                    CElement(void)
                                    {
                                       Cardinality(0);
                                    };
                                    ~CElement(void) {};
   };


Elementクラスは、集合(始域クラス)から配列として呼び出されます。

//+------------------------------------------------------------------+
//| DOMAIN CLASS                                                     |
//+------------------------------------------------------------------+
template <typename T>
class CDomain                       : public CObject
   {
      protected:
      
      int                           cardinal;
      CElement<T>                   elements[];
      
      public:
      
      bool                          Cardinality(int Value) { ... }
      int                           Cardinality() { return(cardinal); }
      
      ...
      
                                    CDomain(void)
                                    {
                                       Cardinality(0);
                                    };
                                    ~CDomain(void) {};
   };


さらに進んで、モノイド群を群とは異なるものとして儀式的に定義する圏クラスだけでなく、射、準同型、そして他の概念の連続体のクラスも定義しました。これらと圏クラスは、モノイドクラスを構成する上でそれ自体重要ではないので、この記事では列挙も考察もしません。モノイドとは、集合、単位元、二項演算の3つであることを思い出してください。二項演算とは何かを定義することから始めれば、選択肢を列挙することで実現できます。過去2回の記事では、これに似たものを使ってきました。

//+------------------------------------------------------------------+
//| Enumeration for Monoid Operations                                |
//+------------------------------------------------------------------+
enum EOperations
  {
      OP_FURTHEST=5,
      OP_CLOSEST=4,
      OP_MOST=3,
      OP_LEAST=2,
      OP_MULTIPLY=1,
      OP_ADD=0
  };


この記事のためにこれを改訂することはしませんが、それぞれのモノイド集合要素に対して、二項演算と考えるものをカスタマイズして定義することができる手段を設定するものであることは言うまでもありません。ここでの可能性は興味深いものです。モノイドクラスに話を移すと、集合(始域)のインスタンスを持つ新しいクラスを定義するのではなく、始域クラスを継承するようにクラスを構成します。これはコード効率がよく、また直感的に、モノイドとは単に二項演算と単位元を持つ始域である、と言えます。

//+------------------------------------------------------------------+
//| Monoid Class                                                     |
//+------------------------------------------------------------------+
template <typename T>
class CMonoid                       : public CDomain<T>
   {
      protected:
      //double                        weights[];
      
      int                           identity;
      EOperations                   operation;
      
      public:
      
      double                        weights[];
      
      ...
      
      void                          Operation(EOperations Value) {  operation=Value; }
      EOperations                   Operation() { return(operation); }
      
      ...
      
                                    CMonoid(){ identity=0; operation=OP_ADD; };
                                    ~CMonoid(){};
   };


このクラスに二項演算と単位元という2つの公理を追加します。ただし、単位元は、始域の要素の配列内にすでに存在するため、繰り返しになる要素の別のインスタンスではありません。そうではなく、単にその配列のインデックスを、ここでの単位元を指すものとして参照するのです。モノイドクラスは、以下の例のように、スクリプトの中で自動ポインタによって初期化することができます。

前回の記事で取り上げたモノイド作用は、このモノイドクラスを継承しています。

しかしモノイド群については、意味的にはモノイドとモノイド群の間にクラスコードの違いはありません。モノイド群による反転の要件は、確認するだけで大丈夫です。この目的のために、モノイド群クラスは以下のような'HasInversion'チェック関数を備えています。

//+------------------------------------------------------------------+
//| Monoid Group Class                                               |
//+------------------------------------------------------------------+
template <typename T>
class CMonoidGroup                 : public CMonoid<T>
   {
      protected:
      
      public:
      
      bool                          HasInversion() 
                                    {  
                                       bool _has_inversion=true;
                                       
                                       for(int i=0;i<this.Cardinality();i++)
                                       {
                                          bool _has_inverse=false;
                                          
                                          for(int ii=0;ii<this.Cardinality();ii++)
                                          {
                                             if(Operate(i,ii)==Identity()){ _has_inverse=true; }
                                          }
                                          
                                          if(!_has_inverse){ _has_inversion=false; break; }
                                       }
                                       
                                       return(_has_inversion); 
                                    }
      
                                    CMonoidGroup(){};
                                    ~CMonoidGroup(){};
   };


さて、前の2つの記事では、モノイドとモノイド作用クラスの要素は、正規化されていないデータを構成することができ、実際に構成していました。つまり、二項演算で使用する前に、公平に比較できる形式に変換する必要がありました。本稿では、この形式を重みと呼ぶことにします。これまでの記事では、これらの重み値は実行時に計算され、使用されていました。ここでは、モノイド群クラスに、これらの重みの値をクラス内で設定、保存、取得するためのパラメータを導入させます。重みはすべてdoubleデータ型となります。

      CMonoidGroup<int> _vg;        //valid inversion group
      CMonoidGroup<int> _ig;        //invalid inversion group
      
      _vg.Weights(5);             //set group size
      _ig.Weights(5);             //set group size
      for(int i=0;i<5;i++)
      { 
         CElement<int> _ve;_ve.Cardinality(1); _ve.Set(0,i-2);
         _vg.Set(i,_ve,true);      //set element
         _vg.SetWeight(i,double(i-2));  //set weight
         
         CElement<int> _ie;_ie.Cardinality(1); _ie.Set(0,i);
         _ig.Set(i,_ie,true);      //set element
         _ig.SetWeight(i,double(i));   //set weight
      }
      
      _vg.Operation(OP_ADD);      //set monoid operation to add
      _vg.Identity(2);            //set identity element index to 2
      
      _ig.Operation(OP_ADD);      //set monoid operation to add
      _ig.Identity(2);            //set identity element index to 2 as above or any index
      
      printf(" it is: "+string(_vg.HasInversion())+", vg has inversion, given the weights. ");
      ArrayPrint(_vg.weights,0,",",0,WHOLE_ARRAY,ARRAYPRINT_LIMIT);
      
      printf(" it is: "+string(_ig.HasInversion())+", ig has inversion, given the weights. ");
      ArrayPrint(_ig.weights,0,",",0,WHOLE_ARRAY,ARRAYPRINT_LIMIT);


このコードを実際に見てみるために、モノイド群のインスタンスを作成し、クラスの関数を使って出力を確認し、どのような出力が得られるか見てみましょう。この記事の全リストに添付されているコードは、単純に集合内の反転を確認しているだけです。すべての要素は、単位元について逆元を持つべきです。

2023.06.16 17:17:41.817 ct_10 (USDJPY.i,M1)it is: true, vg has inversion, given the weights. 
2023.06.16 17:17:41.817 ct_10 (USDJPY.i,M1)-2, -1,0,1,2
2023.06.16 17:17:41.817 ct_10 (USDJPY.i,M1)it is: false, ig has inversion, given the weights. 
2023.06.16 17:17:41.817 ct_10 (USDJPY.i,M1) 0,1,2,3,4


実用的な目的のために、モノイド群'_vg'のサイズは5とされましたが、実際のサイズは無限です。というのも、群のすべての公理に合致するためには、二項演算における数の対は、常に群集合の要素である数になるはずだからです。私たちが使ったものでは、2と1を組み合わせると、集合に存在しない3になります。つまり、「_vg」は未束縛の整数集合(Z)です。


アルゴリズム取引におけるモノイド群の用途

前2回の記事では、モノイドを見始めて以来、モノイドを決定点として取り上げてきました。具体的には、次のような決定がなされています。

-考慮すべき振り返り分析期間の長さ

-使用する時間枠

-適用価格

-使用する指標

-取引方法(トレンドフォローかカウンタートレンドか)

それらを決定点とする際に、これらの各モノイドの集合がトレーダーが直面する可能性のある選択肢を構成しました。実装はされていたものの、これらの記事で明確に言及されていなかったのは、モノイドのそれぞれの集合における各要素の重み付けです。選択するためには、すべての集合要素に対してモノイド二項演算をおこなう前に、集合要素を正規化する必要があります。場合によっては、例えば応用価格のように、取引状況によっては要求されるプライスバーごとの比較は容易ではありませんでした。そのため、プライス作用、あるいはどのような基礎指標を選ぶにせよ、時間とともに変化するものに適応する方法で、これらの集合要素を定量化する方法を見つける必要がありました。つまり、この集合要素の「数値化」が、この記事では重みと呼ばれることになります。

さて、応用編として、要素の値に重み付けをした後、群を使うためには、前回の記事から改訂されたモノイド作用クラスのOperateModulo関数を適用する必要があります。作用クラスに含まれる実際の群の集合は、スクリプトへの入力で定義されたサイズまでの単なる整数のリストなので、リストには載っていません。初期集合に対するモジュロ作用は繰り返しを生成するため、ログに記録されるのはこの群に対する相対集合です。

Operate関数は、モノイド作用クラスのメソッドとして、次のように実装することになります。

      int                           OperateModulo(int Index,int Modulo=1)
                                    {
                                       int _operate=-1;
                                       
                                       if(Index>=0 && Index<this.Cardinality())
                                       {
                                          int _value=int(round(set.weights[Index]));
                                          
                                          _operate=_value%Modulo;
                                       }
                                       
                                       return(_operate);
                                    }


つまり、モノイド集合がより小さな「円形」集合に変換されれば、この小さなモノイドの二項演算は、任意の2つの要素を対にして、単位元から最も遠いものを出力することができます。モノイド群のサイズを設定するには、サイズが奇数である必要があります。

2つの要素が等距離にある場合は、単位元が選ばれます。つまり、ここでのモノイド作用は、ベースとなるモノイド集合を群に正規化することです。そして、モノイド作用の二項演算で、モノイド作用集合の値に基づいて要素を対にして決定します。

この記事では、エキスパートアドバイザー(EA)をコーディングしてテストしているわけではないので、モノイド群の出力を説明するために、前述の5つの機能のそれぞれで、トレーダーが直面しているオプションを含むモノイド集合、これらの集合の値が変換される重み、モノイド群の値をもたらすこれらの重み上のモノイド作用値、およびこのモノイド群の相対集合を出力します。それは、モノイド作用の集合を群として持つために、入力サイズを剰余演算し、重みのすべての値を作用集合に当てはめるからです。剰余演算でこれらの値を正規化すると、繰り返しが発生することになります。そのため、作用セットは厳密には群ではなく、単純に0から始まって入力サイズから1を引いたすべての整数を要素とする群の相対集合となります。

出力ログは、前述のように群に相対する作用セットの要素で締めくくられます。そして、時間枠モノイドから決定モノイドまでの各作用モノイドの計算値を、上で概説した群の公理に従って選択することにより、繰り越すのは読者次第です。群集合の二項演算でもう一度言っておくと、モノイドのように、対になる要素のどれかが単位元であれば、非単位元が出力となり、さらに対になる要素が互いに逆元であれば、出力は単位元となります。

加えて、前2回の記事で検討したのとは対照的に、時間枠から始まり、期間を振り返って選択することは、洞察に値するかもしれません。とはいえ、こうして時間枠モノイドの重みを得ることになります。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void WeighTimeframes(CMonoidGroup<ENUM_TIMEFRAMES> &G)
   {
      for(int i=0;i<G.Cardinality();i++)
      {
         ResetLastError();
         int _value=0;
         ArrayResize(__r,3);//ArrayInitialize(_buffer,0.0);//ArraySetAsSeries(_buffer,true);
         if(CopyRates(_Symbol,__TIMEFRAMES[i],0,3,__r)>=3)
         {
            _value=int(round(10000.0*fabs(__r[0].close-__r[1].close)/fmax(_Point,fabs(__r[0].close-__r[1].close)+fabs(__r[1].close-__r[2].close))));
         }
         else{ printf(__FUNCSIG__+" Failed to copy: "+EnumToString(__TIMEFRAMES[i])+" close prices. err: "+IntegerToString(GetLastError())); }
         
         ResetLastError();
         if(!G.SetWeight(i,_value))
         {
            printf(__FUNCSIG__+" Failed to assign element at index: "+IntegerToString(i)+", for lookback. ERR: "+IntegerToString(GetLastError()));
         }
      }
   }


重み付けはすべて整数形式に正規化されることにご注意ください。モノイド作用によって、群に対する集合に変換する際に剰余演算を使いたいためです。つまり、重みは正のdoubleで、1以上にはならないので、時間枠では、これを0から10,000までの整数に変換します。また、時間枠の入力サイズパラメータ(デフォルトは51)は、群の集合要素である残りを取得するために使用する値となります。残りの値は、モノイド作用クラスの重み配列に格納されます。

そこで、スクリプトをUSDJPTの1分足チャートに接続すると、15.06.2023の時点で、時間枠モノイドの出力はこのようになります。

2023.06.16 17:17:41.818 ct_10 (USDJPY.i,M1)with an input size of: 21 timeframe weights, and their respective monoid action values (group normalised) are: 
2023.06.16 17:17:41.818 ct_10 (USDJPY.i,M1)7098, 8811, 1686, 1782, 1280, 5920, 1030, 5130
2023.06.16 17:17:41.819 ct_10 (USDJPY.i,M1) {(0),(12),(6),(18),(20),(19),(1),(6)}
2023.06.16 17:17:41.819 ct_10 (USDJPY.i,M1) 
2023.06.16 17:17:41.819 ct_10 (USDJPY.i,M1)and action group values (relative set) are: 
2023.06.16 17:17:41.819 ct_10 (USDJPY.i,M1)0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20

過去の記事から少し修正した時間枠を使用している。繰り返しになりますが、自らの研究内容に最適な時間枠を選ぶのは読者次第だ。振り返りモノイドのログを実行すると、出力は以下のようになります。

2023.06.16 17:17:41.819 ct_10 (USDJPY.i,M1)with an input size of: 5 lookback weights, and their respective monoid action values (group normalised) are: 
2023.06.16 17:17:41.820 ct_10 (USDJPY.i,M1)3149, 1116, 3575, 3779, 7164, 8442, 4228, 5756
2023.06.16 17:17:41.820 ct_10 (USDJPY.i,M1) {(4),(1),(0),(4),(4),(2),(3),(1)}
2023.06.16 17:17:41.820 ct_10 (USDJPY.i,M1) 
2023.06.16 17:17:41.820 ct_10 (USDJPY.i,M1)and action group values (relative set) are: 
2023.06.16 17:17:41.820 ct_10 (USDJPY.i,M1) 0,1,2,3,4


上記の出力は、時間枠モノイドで1時間の時間枠が選択されたと仮定しています。群の公理に従って各モノイドで実際の最終選択を繰り返すことは、この記事や添付のコードでは実装されていません。モノイド群におけるこの最初の一歩を、自らの戦略に最適だと思う方向へ探求し、踏み出すかどうかは読者に委ねられています。適用されたプライスプリントについて、振り返り期間を8とすると、ログは以下のようになります。

2023.06.16 17:17:41.820 ct_10 (USDJPY.i,M1)with an input size of: 21 appliedprice weights, and their respective monoid action values (group normalised) are: 
2023.06.16 17:17:41.820 ct_10 (USDJPY.i,M1)1469254, 1586223, 1414566, 2087897
2023.06.16 17:17:41.820 ct_10 (USDJPY.i,M1) {(10),(9),(6),(14)}
2023.06.16 17:17:41.820 ct_10 (USDJPY.i,M1) 
2023.06.16 17:17:41.820 ct_10 (USDJPY.i,M1)and action group values (relative set) are: 
2023.06.16 17:17:41.820 ct_10 (USDJPY.i,M1)0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20


結論

典型的なモノイドに対称性の概念を導入し、モノイド群のすべての元が逆元を持つ必要があるという公理を追加し、さらにミラー要素間の二項演算がモノイド群の単位元を常に出力するように制限することで、モノイド群とは何かを見てきました。これは、モノイド作用について考察した前回の記事に続くものです。

前回の記事にあったような制約付きモノイド集合のモノイド群がトレーダーにとっていかに有益であるかを示唆しました。その記事では、モノイド集合を、ある段階におけるトレーダーの選択の固定プールとみなしました。これは、前回の記事で取り上げた、選択したモノイド集合の拡大が取引のパフォーマンスに与える影響を探ったモノイド作用の方法とは異なるものです。

モノイド群の可能性を「示唆」することに留め、前の2つの記事で紹介したようなモノイド群を使ったEAを紹介することはしませんでした。読者は、ここで言及されたがコード化はしていないモノイド群のルールに従って、それぞれのモノイド群で洗濯を実施することで、この資料をさらに活用することができます。

次回は、圏論のもうひとつの概念に取り組みます。


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

添付されたファイル |
ct_10.mq5 (14.21 KB)
ct_10.mqh (25.69 KB)
MQL5のインタラクティブGUIで取引チャートを改善する(前編):移動可能なGUI (I) MQL5のインタラクティブGUIで取引チャートを改善する(前編):移動可能なGUI (I)
MQL5で動かせるGUIを作成するための包括的なガイドで、取引戦略やユーティリティでのダイナミックなデータ表現の力を解き放ちましょう。チャートイベントのコアコンセプトに触れ、同じチャート上にシンプルで複数の移動可能なGUIをデザインし、実装する方法を学びます。この記事では、GUIに要素を追加し、機能性と美しさを向上させるプロセスについても説明します。
MQL5の圏論(第9回):モノイド作用 MQL5の圏論(第9回):モノイド作用
MQL5における圏論の実装についての連載を続けます。ここでは、前の記事で説明したモノイドを変換する手段としてモノイド作用を継続し、応用の増加につなげます。
MQL5におけるARIMAモデルによる予測 MQL5におけるARIMAモデルによる予測
この記事では、ARIMAモデルを構築するためのCArimaクラスの開発を継続し、予測を可能にする直感的な手法を追加します。
MQL5.comでシグナルプロバイダーとして成功する方法 MQL5.comでシグナルプロバイダーとして成功する方法
この記事の主な目的は、MQL5.comでトップのシグナルプロバイダーになるための手順を簡単かつ正確に説明することです。私の知識と経験に基づいて、優れた戦略を見つけ、テストして最適化する方法など、成功するシグナルプロバイダーになるために何が必要かを説明します。さらに、シグナルの公開、説得力のある説明の作成、シグナルの効果的な宣伝と管理に関するヒントも提供します。