MQL5の圏論(第1回)
はじめに
圏論は、1940年代にアイレンベルグとマックレーンによって生み出された数学の一分野です。アインシュタインの相対性理論によって、世界を見る視点は1つではなく、異なる視点間を変換できることが真の力であると認識されつつある時代に、その発想は生まれました。 そのため、これは、分類の対象そのものにこだわるのではなく、分類対象の相互関係に着目して非常に簡潔な定義を導き出す分類法です。この方法の意義は、ある学問や分野から取り入れた概念が、別の分野でも直感的に理解できたり、適用できたりすることです。しかし、当初は幾何学と代数学の連関を研究するために使われるのが主でした。現在では、その用途は明らかに数学を超えており、この連載では紹介しきれないため、ここではMQLプログラミング言語を使用するトレーダーが利用できる可能性のある用途について述べることにします。
MQL5での取引やエキスパートアドバイザー(EA)の作成において、圏論は、異なる取引戦略、商品、市場状況の関係を分析し理解するために使用することができます。それは、トレーダーが取引システムに共通するパターンや構造を特定し、変化する市場環境に適応できる、より一般的で柔軟な取引アルゴリズムを開発するのに役立ちます。
また、圏論は、取引システムの正しさや一貫性を検証したり、取引行動の正式なモデルを開発する際にも有用です。取引の概念を表現し推論するための明確で厳密な言語を提供することにより、圏論はトレーダーがより信頼性が高く保守性の高いEAを作成したり、他のトレーダーや研究者とより効果的にアイデアや戦略を伝達するのに役立つと思われます。
始域と射
集合の圏と射は圏論の基礎概念です。圏論では、圏は要素の始域(別名「集合」)とそれらの間の射(別名「矢印」「写像」「関数」)の集まりです。本稿では、射の基本単位として矢印(/関数/写像)を用いることにします。これらの射は、圏内の各始域の要素間の関係を符号化したものであり、合成してより複雑な射を形成することができます。
集合は数学の基本概念であり、圏論において重要な役割を担っています。圏論では、特定の「種類」の要素を定義するために始域が使用されるように、これらに言及します。一般的に、始域の圏を研究する場合、その圏の「要素」は始域そのものになります。これらの始域は、通常(常にではない)、射の基礎となる他の要素を含んでいます。この圏の射は始域間の関数であり、ある始域の各要素を他の始域の一意の要素に関連付けるルールです。例えば、2つの始域AとBがあるとすると、AからBへの射は、Aの各要素をBの一意の要素に割り当てるルールです。射がAからBへ走るとき、Aは「始域」、Bは「終域」と呼ばれます。始域内のすべての要素は、終域内の少なくとも1つの要素と関係を持つことになります。始域内の要素はすべて写像されます。ただし、終域の一部の要素は写像されない可能性があります。これは、次のように表記されることが多いです。
//+------------------------------------------------------------------+ //| ELEMENT CLASS | //+------------------------------------------------------------------+ class CElement { protected: int cardinal; vector element; public: bool Cardinality(int Value) { if(Value>=0 && Value<INT_MAX) { cardinal=Value; element.Init(cardinal); return(true); } return(false); } int Cardinality() { return(cardinal); } double Get(int Index) { if(Index>=0 && Index<Cardinality()) { return(element[Index]); } return(EMPTY_VALUE); } bool Set(int Index,double Value) { if(Index>=0 && Index<Cardinality()) { element[Index]=Value; return(true); } return(false); } CElement(void) { Cardinality(0); }; ~CElement(void) {}; };
そこで、上のコードから、まず要素を定義します。これは始域の基本単位で、「集合のメンバー」のようなものだと捉えることができます。この単位は、doubleやintegerなどあらゆるデータ型になれますが、より複雑なデータ型や演算に対応するためには、vector型の方がより柔軟性があると考えられます。スケーラビリティが可能になります。そのサイズであるcardinalパラメータはprotectedで、Cardinality()関数を通してのみアクセスまたは変更することができます。ベクトルへのアクセスを保護することで、ベクトルが変更されるたびに適切なサイズに変更されます。無効なインデックスエラーを防止するために、ベクトル「要素」自体もprotectedなので、Get()とSet()関数を使用したアクセスのみが許可されます。Set()関数は、割り当てが成功したかどうかを確認するためにブール値も返します。
//+------------------------------------------------------------------+ //| DOMAIN CLASS | //+------------------------------------------------------------------+ class CDomain { protected: int cardinal; CElement elements[]; public: bool Cardinality(int Value) { if(Value>=0 && Value<INT_MAX) { cardinal=Value; ArrayResize(elements,cardinal); return(true); } return(false); } int Cardinality() { return(cardinal); } bool Get(int Index,CElement &Element) { if(Index>=0 && Index<Cardinality()) { Element=elements[Index]; return(true); } return(false); } bool Set(int Index,CElement &Value,bool IsNew=false) { if(Index>=0 && Index<Cardinality()) { if(!IsNew||New(Value)<0) { elements[Index]=Value; return(true); }} return(false); } //only unique elements allowed int New(CElement &Value) { bool _new=-1; // for(int o=0; o<cardinal; o++) { if(ElementMatch(Value,elements[o])) { _new=o; break; } } return(_new); } CDomain(void) { Cardinality(0); }; ~CDomain(void) {}; };
始域クラスは、上記の要素クラスを含むものです。その主な変数elements[]とそのサイズcardinalは、すでに述べた理由により、要素クラスと同様にprotectedです。ここで少し違うのは、New()メソッドが追加されていることです。この関数は、始域に追加される新しい対象を確認し、それらが一意であることを確認するのに役立ちます。圏の始域は一意な対象のみを含みます。重複は認められません。
この2つのクラスがあるので、始域を構成してみます。偶数の始域と奇数の始域にしてみましょう。これらのクラスを参照するスクリプトを実行することにします。そのコードは以下のようになります。
//+------------------------------------------------------------------+ //| INPUTS | //+------------------------------------------------------------------+ input int __domain_elements=3; input int __domain_morphisms=5; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //Declare a sets of natural even & odd numbers CDomain _evens,_odds; CreateNumberDomain(_evens,__domain_elements,2); CreateNumberDomain(_odds,__domain_elements,2,1); printf(__FUNCSIG__+" evens are... "+PrintDomain(_evens)); printf(__FUNCSIG__+" odds are... "+PrintDomain(_odds)); }
CreateNumberDomain関数は、始域に自然数を入力するための簡単なメソッドです。すべてのコードはこの記事に添付されていますが、わかりやすくするために、その集合をここに示しています。
PrintSet()関数は、私たちが頻繁に参照するリソースとなるメソッドです。これは、始域の内容を、適切な括弧やカンマで囲み、要素ベクトル構造と始域全体の構造を分離して表示することができるものです。 上記のスクリプトを実行すると、このように表示されるはずです。
2022.12.08 18:49:13.890 ct_1 (EURGBP.ln,MN1) void OnStart() odds are... {(1.0),(3.0),(5.0),(7.0),(9.0),(11.0),(13.0)}
各始域の様々な要素が括弧で囲まれているのは、ベクトルデータ型であって複数の項目を持つ可能性があるためです。この場合、各項目の間にカンマが表示され、括弧が要素の境界を定義するのに役立ちます。
射は圏論における重要な概念であり、始域(複数可)の要素間の関係を符号化するために用いられます。圏論の規則に従って、各射は始域の1つの要素を終域の1つの要素に写像します。これらの関数は合成してより複雑な射を形成することができ、これから説明するように、集合の性質や他の集合との関係を研究するのに利用することができます。
まず、射には大きく分けて5つのタイプがあり、圏内の対象間の関係を記述するのに使われます。代表的なものには、以下のようなものがあります。
- モニック射:これらは単射であり、ソースの始域の各要素を終域の一意な対象に写像します。2つの射が終域の同じ対象に写像されることはありません。この場合、終域内の一部の対象が写像されていない可能性があります。
- エピ射:これらは全射であり、始域の要素が終域のすべての要素に写像されます。つまり、終域内のどの要素も写像されないままになることはありません。この場合も、終域に含まれる要素は始域の集合より少ないのが一般的です。
- 同型:これらは全単射であり、始域の対象と終域の対象との間に一対一の対応関係を確立します。始域の集合と終域の集合は同じ数の対象を持つので、単射かつ全射です。
- 自己準同型:ある要素からそれ自身への射です。これは、恒等写像(すなわち始域に戻るリンクする自己準同型写像のグループ)を構成するものです。
- 自己同型:同型写像と自己準同型写像を組み合わせた写像です。
ここでは、射クラスとその準同型と呼ばれる始域とのグループ化がMQLでどのように実装されるかを見てみましょう。
//+------------------------------------------------------------------+ //| MORPHISM CLASS | //+------------------------------------------------------------------+ class CMorphism { public: int domain; int codomain; CElement morphism; bool Morph(CDomain &D,CDomain &C,CElement &DE,CElement &CE,bool Add=true) { int _d=D.New(DE),_c=C.New(CE); // if(_d>=0 && _c>=0) { if(DE.Cardinality()==CE.Cardinality()) { domain=_d; codomain=_c; morphism.Cardinality(DE.Cardinality()); if(Add) { for(int c=0;c<morphism.Cardinality();c++) { morphism.Set(c,CE.Get(c)-DE.Get(c)); } } else { for(int c=0;c<morphism.Cardinality();c++) { if(DE.Get(c)!=0.0){ morphism.Set(c,CE.Get(c)/DE.Get(c)); } } } } } return(false); } CMorphism(void){ domain=-1; codomain=-1; }; ~CMorphism(void){}; };
射クラスは、始域と終域のインデックスパラメータと、この記事には関係ないMorph関数だけからなる非常に基本的なものです。始域集合と終域集合については、以下のようにアンブレラのHomomorphismクラスに記載されているため、ここでは言及を省略します。
//+------------------------------------------------------------------+ //| HOMO-MORPHISM CLASS | //+------------------------------------------------------------------+ class CHomomorphism { protected: int cardinal; CMorphism morphism[]; public: bool init; CDomain domain; CDomain codomain; bool Cardinality(int DomainIndex,int CodomainIndex,bool Add=true) { bool _morphed=true; if ( !init ) { _morphed=false; return(_morphed); } if ( DomainIndex<0 || DomainIndex>=domain.Cardinality() || CodomainIndex<0 || CodomainIndex>=codomain.Cardinality() ) { _morphed=false; return(_morphed); } for(int m=0;m<cardinal;m++) { if(DomainIndex==morphism[m].domain) { _morphed=false; break; } } if(_morphed) { cardinal++; ArrayResize(morphism,cardinal); CElement _de,_ce; if(domain.Get(DomainIndex,_de) && codomain.Get(CodomainIndex,_ce)) { morphism[cardinal-1].Morph(domain,codomain,_de,_ce,Add); } } return(_morphed); }; int Cardinality() { return(cardinal); }; bool Get(int Index,CMorphism &Morphism) { if(Index>=0 && Index<Cardinality()) { return(true); } return(false); }; bool Set(int Index,CMorphism &Value) { if ( Index>=0 && Index<Cardinality() && Value.domain>=0 && Value.domain<domain.Cardinality() && Value.codomain>=0 && Value.codomain<codomain.Cardinality() ) { if(!MorphismMatch(Index,Value,morphism,Cardinality())) { morphism[Index]=Value; return(true); } } return(false); }; void Init(CDomain &Domain,CDomain &Codomain) { domain=Domain; codomain=Codomain; init=true; } CHomomorphism(void){ init=false; cardinal=0; }; ~CHomomorphism(void){}; };
Homomorphismクラスでは、変数'morphism[]'とそのサイズmorphismsは前述の理由でprotectedになっています。理由は繰り返しません。2つのMorphism()関数とGet()およびSet()関数は、対象クラスと集合クラスの関数と同様だと言えば十分でしょう。ここで追加されたのは、publicのdomain集合とcodomain集合を割り当ててクラスが初期化されているかどうかを示すinitブールパラメータです。morphism[]配列への射の追加は、始域インデックスと終域インデックスを指定することによりおこないます。これらのインデックスは、それぞれの域サイズの範囲内である必要があります。これをクリアしたら、特に始域インデックスが使われていないことを確認する必要があります。圏論のルールに従って、始域内のすべての対象は、終域内の対象に一度だけ写像されなければなりません。つまり、終域インデックスは2回使用できますが、始域インデックスは1回しか使用できません。
少し脱線して、部分始域(部分集合)を見てみましょう。部分集合は、圏論において有用なツールです。始域内の部分始域を列挙したり、ある始域が与えられた始域の部分始域になりうるかどうかを確認する機能は、非常に役立つツールです。この2つを実現する関数を見ていきましょう。
//+------------------------------------------------------------------+ //| Domain Match function | //+------------------------------------------------------------------+ bool DomainMatch(CDomain &A,CDomain &B) { if(A.Cardinality()!=B.Cardinality()) { return(false); } bool _matched=true; for(int o=0; o<A.Cardinality(); o++) { CElement _a,_b; if(A.Get(o,_a) && B.Get(o,_b) && !ElementMatch(_a,_b)) { _matched=false; break; } } return(_matched); } //+------------------------------------------------------------------+ //| Is Subdomain function | //+------------------------------------------------------------------+ bool IsSubdomain(CDomain &Domain,CDomain &SubDomain) { bool _is_subdomain=false; int _subdomain_count=0; CDomain _subdomains[]; GetSubdomains(Domain,_subdomain_count,_subdomains); for(int c=0;c<_subdomain_count;c++) { if(DomainMatch(SubDomain,_subdomains[c])) { _is_subdomain=true; break; } } return(_is_subdomain); }
以下のスクリプトを実行します。
//+------------------------------------------------------------------+ //| INPUTS | //+------------------------------------------------------------------+ input int __domain_elements=3; input int __domain_morphisms=5; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //Declare a sets of natural even & odd numbers CDomain _evens,_odds; CreateNumberDomain(_evens,__domain_elements,2); CreateNumberDomain(_odds,__domain_elements,2,1); printf(__FUNCSIG__+" evens are... "+PrintDomain(_evens)); printf(__FUNCSIG__+" odds are... "+PrintDomain(_odds)); int _subdomain_count=0; CDomain _subdomains[]; GetSubdomains(_evens,_subdomain_count,_subdomains); printf(__FUNCSIG__+" evens subs are... "+IntegerToString(_subdomain_count)); for(int s=0; s<_subdomain_count; s++) { printf(" with: "+PrintDomain(_subdomains[s])+", at: "+IntegerToString(s+1)); } }
ログは次のようになるはずです。
2022.12.08 20:25:21.314 ct_1 (EURGBP.ln,MN1) void OnStart() evens subs are... 7 2022.12.08 20:25:21.314 ct_1 (EURGBP.ln,MN1) with: {(2.0)} 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) , at: 1 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) with: {(4.0)} 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) , at: 2 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) with: {(6.0)} 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) , at: 3 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) with: {(2.0),(4.0)} 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) , at: 4 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) with: {(2.0),(6.0)} 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) , at: 5 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) with: {(4.0),(6.0)} 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) , at: 6 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) with: {(2.0),(4.0),(6.0)} 2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) , at: 7
実際に可能な部分集合の数は、同じく部分集合である空集合を含めると8個になります。
部分集合を確認するための追加コードは、図のように構成することができます。
CDomain _smaller_evens; CreateNumberDomain(_smaller_evens,__domain_elements-2,2); printf(__FUNCSIG__+" smaller evens are... "+PrintDomain(_smaller_evens)); bool _is_subdomain=IsSubdomain(_evens,_smaller_evens);printf(__FUNCSIG__+" it is: "+string(_is_subdomain)+" that 'smaller-evens' is a subdomain of evens. "); _is_subdomain=IsSubdomain(_odds,_smaller_evens);printf(__FUNCSIG__+" it is: "+string(_is_subdomain)+" that 'smaller-evens' is a subdomain of odds. ");
そのログからは、このような結果が得られるはずです。
2022.12.08 20:25:21.315 ct_1 (EURGBP.ln,MN1) void OnStart() smaller evens are... {(2.0)} 2022.12.08 20:25:21.316 ct_1 (EURGBP.ln,MN1) 2022.12.08 20:25:21.316 ct_1 (EURGBP.ln,MN1) void OnStart() it is: true that 'smaller-evens' is a subset of evens. 2022.12.08 20:25:21.316 ct_1 (EURGBP.ln,MN1) void OnStart() it is: false that 'smaller-evens' is a subset of odds.
圏論において、準同型とは、2つの始域間の射群の構造を保存する定義です。これは、要素に定義された関係や操作を保持する機能です。準同型写像は、異なる要素間の関係を研究したり、既存の要素から新しい要素を構築する手段を提供するため、圏論において重要です。
要素間の準同型写像を考慮することで、要素の性質や異なる操作の下での振る舞いを知ることができます。例えば、2つの始域間の準同型写像の存在を利用して、圏同値という概念を定義し、それらの間の関係性を研究することが可能です。
圏論における準同型写像のもう一つの意義は、これらの性質を継承することによって、既存のものから新しいものを構成する手段を提供することです。これらの例と同等性については、この後の記事で取り上げますが、ここでは、なぜこの一見「役に立たない」クラスが定義されているのか、その根拠を示すためにのみ言及します。
ここで、始域と終域に時間と終値の集合を持つ準同型クラスのインスタンスを作成し、圏論を活性化してみましょう。
このインスタンスをPrintHomomorphism()関数を使って表示することにします。これらのカスタム出力関数は、作成した対象、セット、射、準同型を表示する際に非常に便利なものになりそうです。以下はそのコードです。
//+------------------------------------------------------------------+ //| Print Element function | //+------------------------------------------------------------------+ string PrintElement(CElement &O,int Precision=1,bool IsTime=false) { string _element="("; // for(int r=0; r<O.Cardinality(); r++) { if(!IsTime) { _element+=DoubleToString(O.Get(r),Precision); } else if(IsTime) { _element+=TimeToString(datetime(int(O.Get(r)))); } if(r<O.Cardinality()-1){ _element+=","; } } // return(_element+")"); } //+------------------------------------------------------------------+ //| Print Set function | //+------------------------------------------------------------------+ string PrintDomain(CDomain &S,int Precision=1,bool IsTime=false) { string _set="{"; // CElement _e; for(int o=0; o<S.Cardinality(); o++) { S.Get(o,_e); _set+=PrintElement(_e,Precision,IsTime);if(o<S.Cardinality()-1){ _set+=","; } } // return(_set+"}\n"); } //+------------------------------------------------------------------+ //| Print Morphism function | //+------------------------------------------------------------------+ string PrintMorphism(CMorphism &M, CDomain &Domain,CDomain &Codomain,int Precision=1,bool DomainIsTime=false,bool CodomainIsTime=false) { string _morphism=""; // CElement _d,_c; if(Domain.Get(M.domain,_d) && Codomain.Get(M.codomain,_c)) { _morphism=PrintElement(_d,Precision,DomainIsTime); _morphism+="|----->"; _morphism+=PrintElement(_c,Precision,CodomainIsTime); _morphism+="\n"; } // return(_morphism); } //+------------------------------------------------------------------+ //| Print Homomorphism function | //+------------------------------------------------------------------+ string PrintHomomorphism(CHomomorphism &H,int Precision=1,bool DomainIsTime=false,bool CodomainIsTime=false) { string _homomorphism="\n\n"+PrintDomain(H.domain,Precision,DomainIsTime); // _homomorphism+="|\n"; CMorphism _m; for(int m=0;m<H.Cardinality();m++) { if(H.Get(m,_m)) { _homomorphism+=(PrintMorphism(_m,H.domain,H.codomain,Precision,DomainIsTime,CodomainIsTime)); } } // _homomorphism+="|\n"; _homomorphism+=PrintDomain(H.codomain,Precision,CodomainIsTime); // return(_homomorphism); }
ここで注目すべきは、PrecisionとIsTimeの入力です。この2つは、浮動小数点データを表示するための小数点以下の桁数(ベクトルの場合はデフォルト)と、返される文字列の表示目的で対象/ベクトルデータを時間としてキャストする必要があるかどうかを設定します。
そこで、このスクリプトを使って準同型を作成することにします。
//Declare sets to store time & close prices CDomain _time,_close; MqlRates _rates[]; //Fill domain with 5 most recent bar time & MA values from chart if(CopyRates(_Symbol,_Period,0,__domain_morphisms,_rates)>=__domain_morphisms && _time.Cardinality(__domain_morphisms) && _close.Cardinality(__domain_morphisms)) { for(int m=0;m<__domain_morphisms;m++) { //Create uni row element CElement _t,_m; if(_t.Cardinality(1) && _m.Cardinality(1)) { datetime _t_value=_rates[m].time;//iTime(_Symbol,_Period,m); _t.Set(0,double(int(_t_value))); _time.Set(m,_t); double _m_value=_rates[m].close;//iClose(_Symbol,_Period,5);//,m,MODE_SMA,PRICE_CLOSE); _m.Set(0,_m_value); _close.Set(m,_m); } } } //Create homomorphism from time to close CHomomorphism _h;_h.Init(_time,_close); if(_h.init) { //Create 1-1 morphisms from time to MA int _morphisms=0; for(int m=0;m<__domain_morphisms;m++) { if(_h.Cardinality(m,m)){ _morphisms++; } } if(_morphisms>=__domain_morphisms) { printf(__FUNCSIG__+" homomorphism: "+PrintHomomorphism(_h,_Digits,true)); } }
2つの集合「_time」と「_close」を作成します。次に、MqlRates配列からのデータをそれらに入力します。「__set_morphism」は入力パラメータで、集合のサイズを決定します。この場合、「_time」と「_close」は同じ大きさで、入力パラメータと一致します。準同型クラスインスタンス「_h」はこの2つの集合で初期化されます。2つの集合の間には、Morphisms()関数を用いて単純な一対一の射が生成されますが、この関数はbooleanを返すので確認します。trueの場合、「_morphisms」パラメータがインクリメントされ、入力パラメータのサイズになったら、「Print Homomorphism()」関数を使って準同型「_h」をプリントします。ログは次のようになります。
2022.12.08 20:25:21.317 ct_1 (EURGBP.ln,MN1) void OnStart() homomorphism: 2022.12.08 20:25:21.317 ct_1 (EURGBP.ln,MN1) 2022.12.08 20:25:21.317 ct_1 (EURGBP.ln,MN1) {(2022.07.01 00:00),(2022.08.01 00:00),(2022.09.01 00:00),(2022.10.01 00:00),(2022.11.01 00:00)} 2022.12.08 20:25:21.317 ct_1 (EURGBP.ln,MN1) | 2022.12.08 20:25:21.317 ct_1 (EURGBP.ln,MN1) (2022.07.01 00:00)|----->(0.83922) 2022.12.08 20:25:21.317 ct_1 (EURGBP.ln,MN1) (2022.08.01 00:00)|----->(0.86494) 2022.12.08 20:25:21.317 ct_1 (EURGBP.ln,MN1) (2022.09.01 00:00)|----->(0.87820) 2022.12.08 20:25:21.317 ct_1 (EURGBP.ln,MN1) (2022.10.01 00:00)|----->(0.86158) 2022.12.08 20:25:21.317 ct_1 (EURGBP.ln,MN1) (2022.11.01 00:00)|----->(0.87542) 2022.12.08 20:25:21.317 ct_1 (EURGBP.ln,MN1) | 2022.12.08 20:25:21.317 ct_1 (EURGBP.ln,MN1) {(0.83922),(0.86494),(0.87820),(0.86158),(0.87542)}
圏論では、準同型の像集合を列挙することも有益です。像とは単に、始域から写像された対象のみを特徴とする、終域の部分集合を指します。ここでは、以下の関数を用いてこれを列挙します。
//+------------------------------------------------------------------+ //| Image function | //+------------------------------------------------------------------+ void Image(CHomomorphism &H,CSet &Output) { for(int m=0;m<H.Morphisms();m++) { CObject _o; CMorphism _m; if(H.Get(m,_m) && H.codomain.Get(_m.codomain_index,_o)) { bool _matched=false; for(int o=0;o<Output.Objects();o++) { CObject _oo; if(Output.Get(o,_oo) && ObjectMatch(_o,_oo)) { _matched=true; break; } } if(!_matched) { Output.Objects(Output.Objects()+1); Output.Set(Output.Objects()-1,_o); } } } }
このコードの動きを見るために、次のスクリプトを使ってみましょう。
//Create homomorphism from time to close CHomomorphism _h_image;_h_image.Init(_time,_close); if(_h_image.init) { //Create 1-1 morphisms from time to MA int _morphisms=0; for(int m=0;m<__set_morphisms;m++) { int _random_codomain=MathRand()%__set_morphisms; if(_h_image.Morphisms(m,_random_codomain)){ _morphisms++; } } if(_morphisms>=__set_morphisms) { CSet _image;_image.Objects(0); Image(_h_image,_image); printf(__FUNCSIG__+" image from homomorphism: "+PrintSet(_image,_Digits)); } }
ここでやっていることは、「_h_image」という新しい準同型クラスで、以前に作成した2つの集合を使っているだけです。今回の違いは、射を追加する際に、終域の値をランダムに選択するため、繰り返しが発生することです。つまり、「_h」インスタンスの終域集合とは異なるものになります。ログは次を生成するはずです。
2022.12.08 21:55:54.568 ct_1 (EURJPY.ln,H2) void OnStart() image from homomorphism: {(145.847),(145.188)}
2022.12.08 21:55:54.568 ct_1 (EURJPY.ln,H2)
終域には、3つの対象のうち2つしかないので、ここから、与えられた集合から写像された対象を簡単に取り出すことができるのは明らかです。ここから先は、同型写像と自己準同型写像について説明します。
結論
情報を分類するための数学の手法として、ますます力を発揮している「圏論」の入門的な概念について見てきました。要素、領域、射がありました。要素は基礎単位を形成し、通常、圏論における集合では始域と呼ばれる集合のメンバーとして考えられています。この始域は、その要素を通じて他の始域(終域と呼ばれる)と関係を持つことができます。これらの関係は射と呼ばれます。トレーダーにとって、圏論は、時系列の金融情報を分類し、市場の状況を評価・予測するためのツールとして機能することができます。次回は、ここから再開します。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/11849
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索