MQL5の圏論(第2回)
はじめに
前回に引き続き、MQL5での圏論の使用についてです。圏論とは、情報を整理・分類するために特に有効な数学の一分野であると紹介しましたが、ここでもう一度おさらいしておきますと、圏論とは、情報を整理・分類するために特に有効な数学の一分野です。その状況で、圏論の基本的な概念と、それが金融時系列データの分析にどのように適用できるかを説明しました。具体的には、要素、領域、射について検討しました。要素は圏論システムにおける情報の基本単位であり、通常、この連載では始域と呼ばれる集合のメンバーとして考えられています。域は射と呼ばれる処理によって、他の域と関係を持つことができます。今回は、まず、恒等、結合、可換図式という公理を見て、何が圏を構成しているのかを考察することにします。その際、また記事全体を通して、事例を検証するとともに圏以外の事例も取り上げていきます。最後に、オントロジーログについて紹介します。
圏の公理的な定義を掘り下げる前に、圏が「実際に使われている」日常的な例をいくつか見ておくと役に立つかもしれません。始域とそれに含まれる要素についてはすでに定義しました。まず、圏を関連する始域の集まりとすると、次のような例が考えられます。
- 交通手段の圏:自動車、飛行機、電車など様々な交通手段を始域で表します。各始域の要素は、それぞれの種類の輸送機となります。例えば、自動車ではライドシェア、レンタカー、マイカー、タクシー、飛行機ではプライベートジェット、民間機、リース機、鉄道では路面電車、新幹線、蒸気機関車などです。これらの間の射は、完全な旅程を定義するのに役立つでしょう。例えば、空港までライドシェアで行き、そこから飛行機で移動し、新幹線で目的地まで行った場合、それぞれの選択肢をこの圏で簡単に写像することができます。
- 食事のコースの圏:各コースのメニューを始域で表します。例えば、前菜、スープ、メインディッシュ、デザート、チーズの5つのコースがあるとします。この場合、各始域の要素は、それぞれのコースのメニューにある食べ物になります。例えば、前菜のコースは、その始域(メニュー)で構成される選択肢を持つことができます。 人参の蜂蜜、クミン、パプリカ漬け、または。マッシュルームのペコリーノ・ロマーノ、ガーリック、パン粉詰め、または、ブロッコリーとしし唐、玉ねぎのピクルス。同じように、スープ始域には次があるかもしれません。トスカーナ風ホワイトビーンズとローストガーリックのスープ、または、パンプキンセージビスク、または、メロンとバジルの冷製スープ。これらのメニュー項目は、それぞれその始域内の要素を表しています。同様に、これらの間の射は、レストランの客が選ぶ完全な食事を定義するのに役立ちます。例えば、客が人参の蜂蜜漬けを食べた後でかぼちゃを食べ、他のコースも全⁰て食べたとしたら、上記のようにそれぞれの選択肢を圏内に簡単に写像することができます。
- また、別の圏の例として、一週間の娯楽の種類を挙げることができます。 ここでは、スポーツ中継、テレビストリーミング、アミューズメントパークなどが始域になります。各始域の要素としては、以下のようなものが考えられます。スポーツ中継ならNFLやMLB、NBA、テレビストリーミングならNetflixやHBO、AMC、アミューズメントパークならディズニーや動物園、ネイチャーパークです。今回も、スポーツ観戦、テレビ番組、アミューズメントパークなど、それぞれの領域から選ぶことができます。そして、これらの域をまたぐ選択文字列(射)により、この情報を容易に写像することができます。
このように、圏論によって、これらすべて、そして明らかにそれ以上のことを記録できます。でも、何のためにでしょうか。 「下手な職人は道具のせいにする」ということわざがありますが、まさにその通りだと思います。用途は、本当にユーザーが決めるものだからです。私の意見では、圏論は、圏同値 を定量化するのに決定的な役割を果たします。この点については、基本的な概念をある程度理解した上で、次回以降に紹介する予定です。表面的には無関係に見えても、深く調べてみると同一であったり、鏡のように正反対であったりする、異なるテーマに関する2つの別々の圏が発見されるのはこのためと言えば十分でしょう。意思決定時に十分な情報が得られることはほとんどないため、こうした洞察は意思決定に欠かせません。しかし、相関性のある圏があれば、情報のギャップを埋めることができます。
同型写像
同型写像は、圏論における準同型の重要な性質です。写像のもとで対象圏の始域の構造が保存されることを保証するからです。また、ソース圏に含まれる域の代数演算の保存も保証されています。例えば、シャツとパンツを始域とし、シャツのサイズとパンツのサイズを対応させる関数を射とする衣服の圏を考えてみましょう。この圏における準同型写像とは、シャツのサイズとパンツのサイズの対を保存する関数のことです。この圏における同型写像とは、サイズの代数的な組を保存するだけでなく、シャツとパンツのサイズの一対一の対応を確立する関数のことです。つまり、シャツのサイズに対応するパンツのサイズは1つであり、 その逆もまた然りなのです。例えば、シャツのサイズ(例:S、M、L)とパンツのサイズ(例:26、28、30、32).を対応付ける関数を考えてみましょう。この関数は,サイズの対を保存し定義するため,準同型となります(例えば,Sは26と対にできる)。しかし、Sを28や26と一緒に着られることを考えると、シャツとパンツのサイズに一対一の対応関係が成立しないので、同型とは言えません。可逆性はありません。
一方、シャツのサイズ(例:S、M、L)とパンツのサイズ(例:26、28、30、32).のみを対応付ける関数を考えてみましょう。この関数は準同型であるだけでなく、可逆性も認めているため、同型でもあります。
衣服の圏の例では、始域がシャツとパンツで、射がシャツのサイズとパンツのサイズを対応付ける関数である場合、個々の射はすべて本質的に可逆でそこに「同型」であるため、単一の射は同型にはなりえません。同型の性質とは、ある始域から終域への射の群(別名:準同型集合)を扱い、同型の代数的な対の性質を保ったまま、それらの射を群としてすべて反転できる場合にのみ存在するとみなすことができるものです。そのため、ある始域からの形態素をすべて列挙してから同型性を確認することが重要です。可能な限りの射を考慮しないと、2つの対象が同型であるように見えても、実際にはそうでないことがあります。同型の前兆は、始域の濃度です。この値は、両方の始域で同じである必要があります。始域の濃度が一致しない場合、始域間の準同型写像を反転させることはできません。ある始域から同じ共同要素に写像される要素が複数存在することになるからです。したがって、同型性を定義するためには、始域間の射の群が必要です。一般に、シャツとパンツのサイズを一対一に対応させるために、シャツからパンツへ、パンツからシャツへという2つの射が必要です。これらの射は互いの逆でなければなりません。つまり、一方の射がサイズSのシャツをサイズ28のパンツに写す場合、他方の射はサイズ28のパンツをサイズSのシャツに写すはずです。そして、この2つの射を合成すると、始域のための恒等射が得られるはずです。
この圏論における公理要求は、射が自己写像する必要性を推論しています。しかし、圏を定義する上でそれらが適切であるかどうかについては、まだ議論されています。
次に、MQL5における同型写像を説明します。第1回で共有したスクリプトのほとんどを書き直し、テンプレート化するようにしました。要素クラスから圏クラスまで、すべてのクラスでテンプレートデータ型を使用し、柔軟性を持たせています。しかし、圏クラス内で利用可能な型を「列挙」しました。これらは、datetime、string、double、intです。int型はデフォルトで、例えばcolorのようなデータ型が使用されている場合に使用されます。完全なソースコードは本稿末尾に添付されています。これが新しい準同型クラスです。
//+------------------------------------------------------------------+ //| HOMO-MORPHISM CLASS | //+------------------------------------------------------------------+ template <typename TD,typename TC> class CHomomorphism : public CObject { protected: int morphisms; public: CDomain<TD> domain; CDomain<TC> codomain; CMorphism<TD,TC> morphism[]; int Morphisms() { return(morphisms); } bool Morphisms(int Value) { if(Value>=0 && Value<INT_MAX) { morphisms=Value; ArrayResize(morphism,morphisms); return(true); } return(false); } bool Get(int MorphismIndex,CMorphism<TD,TC> &Morphism) { if(MorphismIndex>=0 && MorphismIndex<Morphisms()) { Morphism=morphism[MorphismIndex]; Morphism.domain=domain; Morphism.codomain=codomain; return(true); } return(false); } template <typename TDD,typename TDC> bool Set(int ValueIndex,CMorphism<TDD,TDC> &Value) { if ( (string(typename(TD))!=string(typename(TDD))) || (string(typename(TC))!=string(typename(TDC))) || ) { return(false); } // if(ValueIndex>=0 && ValueIndex<Morphisms()) { Value.domain=domain; Value.codomain=codomain; if(Index(Value)==-1) { morphism[ValueIndex]=Value; return(true); } } return(false); }; template <typename TDD,typename TDC> int Index(CMorphism<TDD,TDC> &Value) { int _index=-1; // if ( (string(typename(TD))!=string(typename(TDD))) || (string(typename(TC))!=string(typename(TDC))) || ) { return(_index); } // for(int m=0; m<morphisms; m++) { if(MorphismMatch(Value,morphism[m])) { _index=m; break; } } return(_index); } CHomomorphism(void){ Morphisms(0); }; ~CHomomorphism(void){}; };
同型かどうかを調べるために、前回と同じ始域を使い、IsIsomorphic関数で実行します。この関数はブール値を返し、trueは成功を、falseは失敗を表します。
//+------------------------------------------------------------------+ //| Get Isomorphisms function | //+------------------------------------------------------------------+ template <typename TD,typename TC> bool IsIsomorphic(CDomain<TD> &A,CDomain<TC> &B,CHomomorphism<TD,TC> &Output[]) { if(A.Cardinality()!=B.Cardinality()) { return(false); } int _cardinal=A.Cardinality(); uint _factorial=MathFactorial(_cardinal); ArrayResize(Output,_factorial); for(uint f=0;f<_factorial;f++) { ArrayResize(Output[f].morphism,_cardinal); // for(int c=0;c<_cardinal;c++) { Output[f].morphism[c].domain=A; Output[f].morphism[c].codomain=B; } } int _index=0; CDomain<TC> _output[];ArrayResize(_output,_factorial); GetIsomorphisms(B, 0, _cardinal-1, _cardinal, _index, _output); for(uint f=0;f<_factorial;f++) { for(int c=0;c<_cardinal;c++) { CElement<TC> _ec; if(_output[f].Get(c,_ec)) { for(int cc=0;cc<_cardinal;cc++) { CElement<TC> _ecc; if(B.Get(cc,_ecc)) { if(ElementMatch(_ec,_ecc)) { if(Output[f].morphism[c].Codomain(cc)) { break; } } } } } if(Output[f].morphism[c].Domain(c)) { } } } return(true); }
しかし、出力される準同型はオンセットで指定する必要があり、この例では変数'「_h_i」を使用しています。この出力値は準同型クラスの配列であり、2つの入力始域間で可能なすべての準同型の列挙が含まれます。
//IDENTITY CHomomorphism<int,int> _h_i[]; //is evens isomorphic to odds? if(IsIsomorphic(_evens,_odds,_h_i)) { printf(__FUNCSIG__+" evens can be isomorphic to odds by up to: "+IntegerToString(ArraySize(_h_i))+" homomorphisms. These could be... "); for(int s=0; s<ArraySize(_h_i); s++) { printf(__FUNCSIG__); string _print=""; for(int ss=0; ss<ArraySize(_h_i[s].morphism); ss++) { _print+=PrintMorphism(_h_i[s].morphism[ss],0); } printf(_print+" at: "+IntegerToString(s)); } }
このコードを実行すると、以下のようなログが表示されるはずです。
2023.01.26 10:42:56.909 ct_2 (EURGBP.ln,H1) void OnStart() evens can be isomorphic to odds by up to: 6 homomorphisms. These could be... 2023.01.26 10:42:56.909 ct_2 (EURGBP.ln,H1) void OnStart() 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) (0)|----->(1) 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) (2)|----->(3) 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) (4)|----->(5) 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) at: 0 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) void OnStart() 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) (0)|----->(1) 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) (2)|----->(5) 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) (4)|----->(3) 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) at: 1 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) void OnStart() 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) (0)|----->(3) 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) (2)|----->(1) 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) (4)|----->(5) 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) at: 2 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) void OnStart() 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) (0)|----->(3) 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) (2)|----->(5) 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) (4)|----->(1) 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) at: 3 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) void OnStart() 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) (0)|----->(5) 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) (2)|----->(3) 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) (4)|----->(1) 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) at: 4 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) void OnStart() 2023.01.26 10:42:56.910 ct_2 (EURGBP.ln,H1) (0)|----->(5) 2023.01.26 10:42:56.911 ct_2 (EURGBP.ln,H1) (2)|----->(1) 2023.01.26 10:42:56.911 ct_2 (EURGBP.ln,H1) (4)|----->(3) 2023.01.26 10:42:56.911 ct_2 (EURGBP.ln,H1) at: 5
以下は偶数始域の場合の出力です。
2023.01.26 10:42:56.899 ct_2 (EURGBP.ln,H1) void OnStart() evens are... {(0),(2),(4)}
以下は奇数始域の場合の出力です。
2023.01.26 10:42:56.899 ct_2 (EURGBP.ln,H1) void OnStart() odds are... {(1),(3),(5)}
結合
圏論において、結合法則の公理は、圏が満たすべき基本的な性質の1つです。例えば、シャツ、パンツ、靴といった特定の衣類を始域とする衣類の圏を例にとると、射は、衣類の適合性に応じてペアリングする関数になります。結合法則の公理は「結合性」とも呼ばれ、射の合成が結合的であることを述べています。つまり、複数の射を合成する場合、その適用順序は最終結果に影響を与えないということです。
例えば、衣服の3つの始域(シャツ、パンツ、靴)すべてにおいて、射「is worn with(一緒に着る)」を考慮します。f:Tシャツ -> ボタンアップシャツ、g:ボタンアップシャツ -> ジーンズ、h:ジーンズ -> スニーカーという射があるとしましょう。結合法則の公理を用いると、これらの射の合成を「h o (g o f) = (h o g) o f」と定義することができます。つまり、射の順番は関係なく、好きなようにグループ分けができるのです。 これにより、複数の射の括弧を計算する必要がなくなり、圏の定義がよりシンプルになります。その代わり、射を順番に関係なくグループ化すれば、最終的な結果は同じになります。
MQL5で実際に見てみましょう。前回の記事で紹介した圏クラスを再度構成する必要があります。
//+------------------------------------------------------------------+ //| CATEGORY CLASS | //+------------------------------------------------------------------+ class CCategory { protected: int domains_datetime; int domains_string; int domains_double; int domains_int; int ontologies; CDomain<datetime> domain_datetime[]; CDomain<string> domain_string[]; CDomain<double> domain_double[]; CDomain<int> domain_int[]; COntology ontology[]; public: int Domain(string T) { if(T=="datetime"){ return(domains_datetime); } else if(T=="string"){ return(domains_string); } else if(T=="double"){ return(domains_double); } return(domains_int); }; bool Domain(string T,int Value) { if(Value>=0 && Value<INT_MAX) { if(T=="datetime") { if(ArrayResize(domain_datetime,Value)>=Value) { domains_datetime=Value; return(true); } } else if(T=="string") { if(ArrayResize(domain_string,Value)>=Value) { domains_string=Value; return(true); } } else if(T=="double") { if(ArrayResize(domain_double,Value)>=Value) { domains_double=Value; return(true); } } else //if(T=="int") { if(ArrayResize(domain_int,Value)>=Value) { domains_int=Value; return(true); } } } return(false); }; int Ontology(){ return(ontologies); }; bool Ontology(int Value){ if(Value>=0 && Value<INT_MAX){ ontologies=Value; ArrayResize(ontology,ontologies); return(true); } return(false); }; template <typename T> bool Set(int ValueIndex,CDomain<T> &Value) { if(Index(Value)==-1 && ValueIndex>=0) { if ( ValueIndex<Domain(string(typename(T))) || (ValueIndex>=Domain(string(typename(T))) && Domain(string(typename(T)),ValueIndex+1)) ) { if(string(typename(T))=="datetime") { domain_datetime[ValueIndex]=Value; return(true); } else if(string(typename(T))=="string") { domain_string[ValueIndex]=Value; return(true); } else if(string(typename(T))=="double") { domain_double[ValueIndex]=Value; return(true); } else //if(string(typename(T))=="int") { domain_int[ValueIndex]=Value; return(true); } } } // return(false); }; template <typename T> bool Get(int DomainIndex,CDomain<T> &D) { if(DomainIndex>=0 && DomainIndex<Domain(string(typename(T)))) { if(string(typename(T))=="datetime") { D=domain_datetime[DomainIndex]; return(true); } else if(string(typename(T))=="string") { D=domain_string[DomainIndex]; return(true); } else if(string(typename(T))=="double") { D=domain_double[DomainIndex]; return(true); } else //if(string(typename(T))=="int") { D=domain_int[DomainIndex]; return(true); } } return(false); }; bool Set(int ValueIndex,COntology &Value) { if ( ValueIndex>=0 && ValueIndex<Ontology() ) { ontology[ValueIndex]=Value; return(true); } else if(ValueIndex>=Ontology()) { if(Ontology(Ontology()+1)) { ontology[Ontology()-1]=Value; return(true); } } // return(false); }; bool Get(int OntologyIndex,COntology &O) { if(OntologyIndex>=0 && OntologyIndex<Ontology()) { O=ontology[OntologyIndex]; return(true); } return(false); }; template <typename T> int Index(CDomain<T> &Value) { int _index=-1; // for(int d=0; d<Domain(string(typename(T))); d++) { if(string(typename(T))=="string") { if(DomainMatch(Value,domain_string[d])) { _index=d; break; } } else if(string(typename(T))=="datetime") { if(DomainMatch(Value,domain_int[d])) { _index=d; break; } } else if(string(typename(T))=="double") { if(DomainMatch(Value,domain_double[d])) { _index=d; break; } } else if(string(typename(T))=="int") { if(DomainMatch(Value,domain_int[d])) { _index=d; break; } } } return(_index); } int Index(COntology &Value) { int _index=-1; // for(int o=0; o<Ontology(); o++) { if(!OntologyMatch(Value,ontology[o])) { _index=o; break; } } return(_index); } CCategory() { domains_datetime=0; domains_string=0; domains_double=0; domains_int=0; ontologies=0; }; ~CCategory() { }; };
許容される「列挙型」データ型に注目してください。今後の記事で、これらを1つの配列に対象として統合することで、よりすっきりさせようと思っています。FillDomain関数も追加されました。これは、以前の記事で始域に自然数を代入していた関数です。
//+------------------------------------------------------------------+ //| Fill Domain(Set) with one-cardinal elements from input E array. | //+------------------------------------------------------------------+ template <typename TD,typename TE> void FillDomain(CDomain<TD> &D,CElement<TE> &E[]) { if(string(typename(TD))!=string(typename(TE))) { return; } int _cardinal=ArraySize(E); // if(_cardinal<0||INT_MAX<=_cardinal) { return; } //Set its cardinal to input array size if(D.Cardinality(_cardinal)) { for(int c=0;c<_cardinal;c++) { D.Set(c,E[c],true); } } }
そこで、結合性を確認する圏「_ca」を作成します。次に、文字列型の3つの単純な集合を宣言し、これらにそれぞれ衣服の種類を入力します。これらの集合(配列)を新しい要素配列(_et、_ep、_es)にコピーし、これらの要素をそれぞれ自分の始域(_dt、_dp、_ds)に追加していくのです。その上で、圏の始域数を3つに設定し、各インデックスに服の種類の要素を入力して新たに作成した始域を設定する作業を進めました。
//ASSOCIATION CCategory _ca; string _tops[__EA]={"T-shirt","button-up","polo","sweatshirt","tank top"}; //domain 0 string _pants[__EA]={"jeans","slacks","khakis","sweatpants","shorts"}; //domain 1 string _shoes[__EA]={"sneakers","dress shoes","loafers","running shoes","sandals"}; //domain 2 CElement<string> _et[];ArrayResize(_et,__EA); CElement<string> _ep[];ArrayResize(_ep,__EA); CElement<string> _es[];ArrayResize(_es,__EA); for(int e=0;e<__EA;e++) { _et[e].Cardinality(1); _et[e].Set(0,_tops[e]); _ep[e].Cardinality(1); _ep[e].Set(0,_pants[e]); _es[e].Cardinality(1); _es[e].Set(0,_shoes[e]); } CDomain<string> _dt,_dp,_ds; FillDomain(_dt,_et);FillDomain(_dp,_ep);FillDomain(_ds,_es); // if(_ca.Domain("string",__DA))//resize domains array to 3 { if(_ca.Set(0,_dt) && _ca.Set(1,_dp) && _ca.Set(2,_ds))//assign each filled domain above to a spot (index) within the category { if(_ca.Domain("string")==__DA)//check domains count { for(int e=0;e<__EA;e++) { COntology _o_01_2; CMorphism<string,string> _m1_01_2,_m2_01_2; SetCategory(_ca,0,1,e,e,_o_01_2," is worn with ",_m1_01_2); SetCategory(_ca,1,2,e,e,_o_01_2," is worn with ",_m2_01_2,ONTOLOGY_POST);printf(__FUNCSIG__+" (0 & 1) followed by 2 Log is: "+_o_01_2.ontology); COntology _o_0_12; CMorphism<string,string> _m1_0_12,_m2_0_12; SetCategory(_ca,1,2,e,e,_o_0_12," is worn with ",_m1_0_12); SetCategory(_ca,0,2,e,e,_o_0_12," is worn with ",_m2_0_12,ONTOLOGY_PRE);printf(__FUNCSIG__+" 0 following (1 & 2) Log is: "+_o_0_12.ontology); } } } }
結合性を確認するために、オントロジーログに射の結果を出力することにします。このために、クラス「COntology」を作成します。以下、同名の列挙と構造体を用いてその概要を説明します。
//+------------------------------------------------------------------+ //| ONTOLOGY ENUM | //+------------------------------------------------------------------+ enum EOntology { ONTOLOGY_PRE=-1, ONTOLOGY_NEW=0, ONTOLOGY_POST=1 }; //+------------------------------------------------------------------+ //| ONTOLOGY STRUCT | //+------------------------------------------------------------------+ struct SOntology { int in; int out; SOntology() { in=-1; out=-1; }; ~SOntology(){}; }; //+------------------------------------------------------------------+ //| ONTOLOGY CLASS | //+------------------------------------------------------------------+ class COntology { protected: int facts; SOntology types[]; SOntology universe[]; public: string ontology; int Facts() { return(facts); } bool Facts(int Value) { if(Value>=0 && Value<INT_MAX) { facts=Value; ArrayResize(types,facts); ArrayResize(universe,facts); return(true); } return(false); } bool GetType(int TypeIndex,int &TypeIn,int &TypeOut) { if(TypeIndex>=0 && TypeIndex<Facts()) { TypeIn=types[TypeIndex].in; TypeOut=types[TypeIndex].out; return(true); } return(false); } bool SetType(int ValueIndex,int ValueIn,int ValueOut) { if(ValueIndex>=0 && ValueIndex<Facts()) { types[ValueIndex].in=ValueIn; types[ValueIndex].out=ValueOut; return(true); } else if(ValueIndex>=0 && ValueIndex>=Facts() && ValueIndex<INT_MAX-1) { if(Facts(ValueIndex+1)) { types[ValueIndex].in=ValueIn; types[ValueIndex].out=ValueOut; return(true); } } return(false); } bool GetUniverse(int UniverseIndex,int &UniverseIn,int &UniverseOut) { if(UniverseIndex>=0 && UniverseIndex<Facts()) { UniverseIn=universe[UniverseIndex].in; UniverseOut=universe[UniverseIndex].out; return(true); } return(false); } bool SetUniverse(int ValueIndex,int ValueIn,int ValueOut) { if(ValueIndex>=0 && ValueIndex<Facts()) { universe[ValueIndex].in=ValueIn; universe[ValueIndex].out=ValueOut; return(true); } else if(ValueIndex>=0 && ValueIndex>=Facts() && ValueIndex<INT_MAX-1) { if(Facts(ValueIndex+1)) { universe[ValueIndex].in=ValueIn; universe[ValueIndex].out=ValueOut; return(true); } } return(false); } string old_hash; string new_hash; COntology() { ontology=""; facts=0; ArrayResize(types,facts); ArrayResize(universe,facts); old_hash=""; new_hash=""; }; ~COntology(){}; };
オントロジーの出力から、スクリプトを実行すると、以下のようなログが出力されるはずです。
2023.01.26 10:42:56.911 ct_2 (EURGBP.ln,H1) void OnStart() (0 & 1) followed by 2 Log is: T-shirt is worn with jeans is worn with sneakers 2023.01.26 10:42:56.912 ct_2 (EURGBP.ln,H1) void OnStart() 0 following (1 & 2) Log is: T-shirt is worn with jeans is worn with sneakers 2023.01.26 10:42:56.912 ct_2 (EURGBP.ln,H1) void OnStart() (0 & 1) followed by 2 Log is: button-up is worn with slacks is worn with dress shoes 2023.01.26 10:42:56.912 ct_2 (EURGBP.ln,H1) void OnStart() 0 following (1 & 2) Log is: button-up is worn with slacks is worn with dress shoes 2023.01.26 10:42:56.912 ct_2 (EURGBP.ln,H1) void OnStart() (0 & 1) followed by 2 Log is: polo is worn with khakis is worn with loafers 2023.01.26 10:42:56.912 ct_2 (EURGBP.ln,H1) void OnStart() 0 following (1 & 2) Log is: polo is worn with khakis is worn with loafers 2023.01.26 10:42:56.912 ct_2 (EURGBP.ln,H1) void OnStart() (0 & 1) followed by 2 Log is: sweatshirt is worn with sweatpants is worn with running shoes 2023.01.26 10:42:56.912 ct_2 (EURGBP.ln,H1) void OnStart() 0 following (1 & 2) Log is: sweatshirt is worn with sweatpants is worn with running shoes 2023.01.26 10:42:56.912 ct_2 (EURGBP.ln,H1) void OnStart() (0 & 1) followed by 2 Log is: tank top is worn with shorts is worn with sandals 2023.01.26 10:42:56.913 ct_2 (EURGBP.ln,H1) void OnStart() 0 following (1 & 2) Log is: tank top is worn with shorts is worn with sandals
まとめると、結合の公理により、射の合成を結合させることで、複数の射を定義する際に括弧を計算する必要がなくなり、簡単に圏を定義することができるようになったのです。これにより、圏内の対象、ここではシャツ、パンツ、靴の3つの始域の衣服の関係をより簡単に理解することができます。
可換図式
可換図式は、圏における2つの射の関係を表す図です。これは、ドメイン、射、およびそれらの合成のセットで構成され、射が交換されることを示す特定の方法で配置されます。つまり、それらが合成される順序は重要ではありません。これは、圏の任意の3つのドメインA、B、Cとそれらの射fについて次であることを示唆します。A -> B、g:B→C、h:A -> C; これらの射の合成は、以下を満たす必要があります。
f o g = h
FXの価格を使った計算の例としては裁定取引が考えられます。EURUSD、EURJPY、USDJPYの価格の関係などです。この例では、始域は通貨そのもの(EUR、USD、JPY)、射は為替レートに基づいてある通貨を別の通貨に変換するプロセスを表すことができます。例えば、f:EUR→USDをEURUSDの為替レートでEURからUSDに変換する過程を表す射とし、g:USD→JPYをUSDJPYの為替レートでUSDからJPYに変換する過程を表す射とし、h:EUR->JPYを、EURからJPYへの変換を表す射とします。上記の交換則に従うと「f o g = h」で、これは次のように変換されます。
[EURUSDレートでEURをUSDに変換し、そのUSDをUSDJPYレートでJPYに変換する] = [EURJPYレートにてEURをJPYに変換する]
これをMQL5で説明しましょう。ここでは、圏「_cc」を宣言し、それぞれ2つの通貨を持つ3つの始域を登録することにします。始域は、アセットアロケーションにおけるポートフォリオと考えることができます。結合の公理と同様に、以下のようにオントロジーログを使って圏値を埋めて確認します。
//COMMUTATION CCategory _cc; string _a[__EC]={"EUR","GBP"}; //domain 0 string _b[__EC]={"USD","CAD"}; //domain 1 string _c[__EC]={"CHF","JPY"}; //domain 2 CElement<string> _e_a[];ArrayResize(_e_a,__EC); CElement<string> _e_b[];ArrayResize(_e_b,__EC); CElement<string> _e_c[];ArrayResize(_e_c,__EC); for(int e=0;e<__EC;e++) { _e_a[e].Cardinality(1); _e_a[e].Set(0,_a[e]); _e_b[e].Cardinality(1); _e_b[e].Set(0,_b[e]); _e_c[e].Cardinality(1); _e_c[e].Set(0,_c[e]); } CDomain<string> _d_a,_d_b,_d_c; FillDomain(_d_a,_e_a);FillDomain(_d_b,_e_b);FillDomain(_d_c,_e_c); // if(_cc.Domain("string",__DC))//resize domains array to 3 { if(_cc.Set(0,_d_a) && _cc.Set(1,_d_b) && _cc.Set(2,_d_c))//assign each filled domain above to a spot (index) within the category { if(_cc.Domain("string")==__DC)//check domains count { for(int e=0;e<__EC;e++) { COntology _o_ab_bc; string _ab=_a[e]+_b[e],_bc=_b[e]+_c[e]; double _ab_bid=SymbolInfoDouble(_ab,SYMBOL_BID),_bc_bid=SymbolInfoDouble(_bc,SYMBOL_BID); string _aspect_ab=" is exchanged at: "+DoubleToString(_ab_bid,(int)SymbolInfoInteger(_ab,SYMBOL_DIGITS))+", for: "; string _aspect_bc=" is exchanged at: "+DoubleToString(_bc_bid,(int)SymbolInfoInteger(_bc,SYMBOL_DIGITS))+", for: "; CMorphism<string,string> _m_ab,_m_bc; SetCategory(_cc,0,1,e,e,_o_ab_bc,_aspect_ab,_m_ab); SetCategory(_cc,1,2,e,e,_o_ab_bc,_aspect_bc,_m_bc,ONTOLOGY_POST);printf(__FUNCSIG__+" a to b then b to c logs: "+_o_ab_bc.ontology); COntology _o_ac; string _ac=_a[e]+_c[e]; string _aspect_ac=" is exchanged at: "+DoubleToString(SymbolInfoDouble(_ac,SYMBOL_BID),(int)SymbolInfoInteger(_ac,SYMBOL_DIGITS))+", for: "; CMorphism<string,string> _m_ac; SetCategory(_cc,0,2,e,e,_o_ac,_aspect_ac,_m_ac);printf(__FUNCSIG__+" a to c logs: "+_o_ac.ontology+" vs product of bid rate for ab and bc of: "+DoubleToString(_ab_bid*_bc_bid,(int)SymbolInfoInteger(_ac,SYMBOL_DIGITS)));//ontology } } } }
可換な射「_m_ac」と射「_m_ab」と「_m_bc」から得られる2つのレートの積を比較することによって、交換の有無を確認するのです。不一致がありますが、これらは主に証券会社の履歴データの質、スプレッドの考慮(買値のみ使用)に起因するものであります。このスクリプトを実行すると、以下のようなログが出力されるはずです。
2023.01.26 17:27:19.093 ct_2 (EURGBP.ln,H1) void OnStart() a to b then b to c logs: EUR is exchanged at: 1.08966, for: USD is exchanged at: 0.91723, for: CHF 2023.01.26 17:27:19.093 ct_2 (EURGBP.ln,H1) void OnStart() a to c logs: EUR is exchanged at: 0.99945, for: CHF vs product of bid rate for ab and bc of: 0.99947 2023.01.26 17:27:19.093 ct_2 (EURGBP.ln,H1) void OnStart() a to b then b to c logs: GBP is exchanged at: 1.65097, for: CAD is exchanged at: 97.663, for: JPY 2023.01.26 17:27:19.093 ct_2 (EURGBP.ln,H1) void OnStart() a to c logs: GBP is exchanged at: 161.250, for: JPY vs product of bid rate for ab and bc of: 161.239
オントロジーログ
オントロジーとは、ある圏を主観的に要約したものです。それらは、与えられた圏における射を介した領域の構造と関係を解釈する方法を提供するため、圏論において重要な道具として機能します。言い換えれば、それらは始域の種類と始域内の射を記述する数学的構造(またはデータモデル、データベーススキーマに相当)です。オントロジーには以下の通り、いくつかの種類があります。- 分類学的オントロジー:対象とそのサブクラスの集合を記述し、それらの間の関係を「is-a」の関係で定義する
- 階層型オントロジー:対象の集合とその性質を記述し、対象間の関係を「part-of」関係で定義する
- 関係オントロジー:対象の集合とその関係を記述し、対象間の関係を任意の二項関係で定義する
FX取引システムの場合、関係オントロジーが最も適切かもしれません。これにより、様々なタイプの取引戦略や、特定の市場条件下でどの戦略が最も効果的であるかといった取引戦略間の関係をモデル化することができるようになります。さらに、関係オントロジーは、取引システムの開発に有用な履歴データなどの追加情報を容易に拡張することができます。
関係オントロジーを圏論で始域横断的に使用する場合の動作は、金融市場の様々な例で実証されています。ここでは5つの事例を紹介します。
- 通貨ペア:FX市場は、2つの通貨を組み合わせて取引される通貨ペアで構成されています。これらの通貨間の関係は、通貨ペアを始域、為替レートをそれらをつなぐ射と見立てた関係オントロジーを用いてモデル化することができます。この例として、すでに上記の可換図が共有されています。
- 時系列データ:FXの価格は時系列データとして表現されることが多く、時系列データを始域、価格の時間的変化を射と見立てた関係オントロジーを用いてモデル化することができます。
- テクニカル指標:FXの価格分析には、移動平均や相対力指数などのテクニカル指標を利用することができます。これらの指標は、指標を始域、指標値を射と見立てた関係オントロジーを用いてモデル化することができます。
- 取引戦略:トレーダーはしばしば、トレンドフォローやモメンタムベースの戦略など、様々な取引戦略を用います。これらの戦略は、取引戦略を始域、戦略に基づいて実行される取引を射と見立てた関係オントロジーを使用してモデル化することができます。
- オーダーブック:オーダーブックは、FX市場で発注されたすべての売買注文を記録したものです。これは、(証券会社が提供する場合)オーダーブックを始域、発注を射としてみ立てた関係オントロジーを使ってモデル化することができます。
全体として、関係オントロジーは、複雑なシステムと関係を明確かつ構造化された方法でモデル化し分析することができるため、取引の域間で使用する場合、圏論に有用です。 オントロジーは、圏の主観的な見方を示すために「タイプ」と「アスペクト」を採用しています。タイプとは、特定の圏に属する始域の群と考えることができます。単純なものと複合的なものが可能です。単純な場合は、1つの始域だけが含まれます。複合の場合は、複数の始域をまとめます。アスペクトは、タイプに対する、圏内の始域に対する射のようなものです。 FX市場における通貨ペアに基づく関係オントロジーは、以下のようにモデル化することができます。
タイプ
- 通貨ペア:通貨ペアはこのオントロジーの主要なタイプであり、互いに取引されている2つの通貨を表します。例えば、「EUR/USD」という通貨ペアは、ユーロと米ドルの為替レートを表しています。
- 為替レート:為替レートは、ある通貨を別の通貨に交換する際のレートです。これは、通貨ペア対象間の射を表します。例えば、「EUR/USD」通貨ペアの為替レートが1.20の場合、1ユーロは1.20米ドルと交換できます。
- 時間:時間は、為替レートが適用される時刻を表すために使用されるタイプです。為替レート射と時系列データの接続に使用することができます。
アスペクト
- 為替レートの履歴:異なる時点で記録された為替レートです。FX市場のトレンドやパターンを分析するために使用することができます。
- ボラティリティ(揮発性):為替レートが時間の経過とともにどの程度変化するかを示す指標です。特定の通貨ペアに関連するリスクを評価するために使用することができます。
- 相関:2つの通貨ペアの関係を示す指標です。FX市場における分散投資やヘッジの機会を特定するために使用することができます。
全体として、この関係オントロジーは、通貨ペア、為替レート、時間、FX市場におけるその他の側面間の関係を、明確かつ構造的に表現することを可能にします。市場のダイナミクスを分析し、情報に基づいた意思決定をおこなうために使用することができます。 グロタンディーク宇宙は、圏論のオントロジーの中で鍵となるuniverse of typesの概念を定義するために使われます。universe of typesは、与えられた圏内のタイプの集合です。タイプの並べ替えが可能になり、「集合の集合」の問題に対応できるようになります。
オントロジーを実際に使ってみるために、金融業界の例を見てみましょう。証券取引委員会(SEC)とヘッジファンドといった異なる主体が、同じ圏の財務データを異なる方法で整理・分類するためにオントロジーを使用することがあります。そのため、各エンティティが使用するオントロジーで表現されるデータの種類や側面が異なることがあります。 SECの観点からすると、オントロジーは、コンプライアンスや規制の問題に焦点を当てた、証券や取引活動に関連するデータを整理するために使用されるかもしれません。これには、有価証券、取引行為、インサイダー取引、証券取引法違反などの種類が含まれる可能性があります。 オントロジーには、調査や罰則などのコンプライアンスやエンフォースメントに関連する側面も含まれるかもしれません。 ヘッジファンドの観点からは、同じ圏のオントロジーを使って、投資戦略、ポートフォリオ管理、パフォーマンス指標に関連するデータを整理することができるかもしれません。これには、投資戦略、ポートフォリオ管理、パフォーマンス指標、リスク管理などのタイプが含まれる可能性があります。 オントロジーには、運用資産やファンドマネージャなど、ファンドの運用や管理に関連する側面も含まれるかもしれません。 このように、両者は財務データを整理するためにオントロジーを使用しているかもしれませんが、オントロジーで表現されるタイプやアスペクトは、SECとヘッジファンドの異なる目標、視点、関心を反映して異なるものです。つまり、SECのオントロジーは証券取引におけるコンプライアンス、規制、違反に焦点を当て、ヘッジファンドのオントロジーは投資、リスクマネジメント、ポートフォリオマネジメントに焦点を当てているのです。
次にFX内でさらにタイプを説明すると、単純タイプと複合タイプは、市場のさまざまな側面を表すために使用できるデータのタイプを指します。 単純タイプは、単一の値で表現できる基本的なデータタイプです。例えば、FXでは、2つの通貨間の為替レートは、1.20(基準通貨1単位と気配値通貨1.20単位を交換できることを意味する)のような単一の値で表される単純タイプとみなすことができます。一方、複合タイプは、複数の値や他のデータタイプから構成されるデータタイプです。例えば、ローソク足チャートは、一定期間の始値、終値、最高値、最安値など複数の値で構成されているため、複合タイプといえます。アスペクトについては、2つの単純タイプを結ぶものが単純アスペクトです。例えば、2つの通貨間の為替レートは単純アスペクトといえます。複合タイプとは、2つの複合タイプ、または複合タイプと単純タイプを結びつけたもので、例えば、取引戦略は、戦略とそれに基づいて実行される取引を結びつけるので、複合タイプと考えることができます。 全体として、単純タイプとアスペクトはオントロジーの基本的な情報を表現するための基本的な構成要素である一方、複合タイプとアスペクトは複数の値や他のデータタイプからなるより複雑なデータタイプで、モデル化されるシステムのより複雑な側面を表現するために使用することができます。
オントロジーログは、興味のある始域に論理構造を与えることで、知識を整理し表現する方法です。これは、特定の始域、下位始域、タスク、インスタンス、またはプロセス内の概念、特性、および関係を定義するのに役立ちます。 オントロジーログとは、特定の始域内に存在する一連の概念、性質、関係を正式に表現したもので、その始域内の知識の構造を定義し、知識の一貫した論理的な表現を提供するために使用されます。これにより、機械や人間が始域内の情報を理解し、利用することが容易になります。オントロジーログは、人工知能、自然言語処理、知識表現、情報科学など、さまざまな分野で利用することができます。一般に、上位オントロジーや始域オントロジーなど、他の種類のオントロジーと組み合わせて、始域の包括的なビューを提供するために使用されます。オントロジーログは通常、OWL (Web Ontology Language)やRDFRDF (Resource Description Framework)といった機械が読みやすい形式で表現され、機械が容易に処理・利用できるようになっています。また、人間が読むことができるため、オントロジーログで表現された情報を理解し、利用することが容易になります。
圏論の文脈では、オントロジーログは、関心のある始域に論理構造を与えることによって、知識を整理し表現するために使用されます。これは、特定の始域、下位始域、タスク、インスタンス、またはプロセス内の概念、特性、および関係を定義するのに役立ちます。 圏論とは、数学システムの構造を扱う数学の一分野であり、対象と射の関係をモデル化するための枠組みを提供するものです。オントロジーログは、圏論の文脈で使われる場合、始域内の知識を構造化された論理的な方法で整理し、表現する方法を提供します。
圏論でよく使われるオントロジーログには、次のような種類があります。
- 上位オントロジー:始域の上位ビューを提供する一般的なオントロジーであり、複数の下位始域にまたがって適用される最も一般的な概念と関係を定義するものです。異なるオントロジー間で統一的な構造を提供するための共通の方法です。
- 始域オントロジー:より大きな始域の中の特定の下位始域に焦点を当てた特定のオントロジーのことで、その下位始域に特有の概念や関係を定義します。
- タスクオントロジー:始域内の特定のタスクや問題に焦点を当てた特定のオントロジーのことで、そのタスクや問題に特有の概念や関係を定義します。
- インスタンスオントロジー:特定の概念やクラスのインスタンスに焦点を当てた特定のオントロジーのことで、それらのインスタンスに固有のプロパティと関係を定義します。
- プロセスオントロジー:始域内で発生するプロセスやアクションに焦点を当てた特定のオントロジーのことで、それらのプロセスやアクションに特有の概念や関係を定義します。
このようなタイプのオントロジーログは、始域の知識と関係を構造化し、情報の理解、表現、操作を容易にするために使用されます。このオントロジーは、始域内の知識を一貫性をもって論理的に表現するのに役立ち、関係オントロジーと組み合わせて、始域の包括的なビューを提供することができます。
プロセスオントロジーログは、始域内で発生するプロセスやアクションに焦点を当てたオントロジーログの特殊なタイプで、それらのプロセスやアクションに特有の概念や関係を定義します。このように、プロセスオントロジーログで使用できるアスペクトの種類は、特定の始域とモデル化されるプロセスまたはアクションに依存します。ここでは、プロセスオントロジーログで使用される可能性のあるアスペクトの例をいくつか紹介します。
- 出入力:プロセスの出入力のアスペクトは、プロセスを開始するために必要な資源、素材、または情報、およびプロセスによって生成される最終製品、サービス、または情報を定義するために使用することができます。
- ステップまたはフェーズ:プロセスはより小さなステップやフェーズに分解することができ、各ステップはそのステップの行動や目的を表す概念として定義することができます。また、ステップ間の関係もモデル化することができます。
- アクター:アクターとは、人、機械、組織など、プロセスに関与する主体または媒体のことです。プロセスにおけるアクター間の関係とその役割を定義することができます。
- 制約:制約とは、プロセスが遵守しなければならない規則、規制、制限のことです。このアスペクトを利用して、プロセスが正常に完了するために満たさなければならない条件や要件を定義することができます。
- メトリクス:メトリクスとは、効率、品質、コストなど、プロセスのパフォーマンスを評価するための測定値や指標のことです。このアスペクトは、プロセスを評価するために使用される測定値や指標、およびそれらの算出方法を定義するために使用することができます。
- 時間的アスペクト:時間的アスペクトとは、処理の開始・終了時刻、継続時間、頻度など、処理のタイミングを指します。このアスペクトは、プロセスの時間的側面をモデル化するために使用することができます。
全体として、プロセスオントロジーログは、出入力、ステップまたはフェーズ、アクター、制約、メトリクス、時間的アスペクトなどのプロセスの様々な側面とそれらの間の関係を、構造的かつ論理的にモデル化し、情報の理解、表現、操作を容易にすることができます。 FX市場の圏におけるプロセスオントロジーの例として、取引の実行プロセスが考えられます。プロセスオントロジーは、取引を実行するプロセスに特有の概念と関係を定義するものです。このプロセスオントロジーのアスペクトをどのようにモデル化するか、その一例を紹介します。
- 出入力:このプロセスの入力には、取引戦略、取引通貨ペア、取引口座残高が含まれます。プロセスの出力は、約定した取引です。
- ステップまたはフェーズ:このプロセスは、通貨ペアの選択、取引パラメータの設定、注文の発注、取引の監視など、より小さなステップまたはフェーズに分けることができます。
- アクター:このプロセスには、トレーダー、取引プラットフォーム、市場などのアクターが関与します。トレーダーはプロセスを開始する主体であり、取引プラットフォームは取引を実行するためのツールであり、市場は取引がおこなわれる環境です。
- 制約:制約には、証拠金、レバレッジ、リスク管理に関する規制が含まれます。取引を成功させるためには、これらの制約を遵守する必要があります。
- メトリクス:プロセスのパフォーマンスを評価するために使用されるメトリクスには、取引の利益と損失、リスクとリターンの比率、および取引の成功率が含まれます。
- 時間的アスペクト:時間的アスペクトとは、取引の開始・終了時刻、取引時間、取引頻度など、処理のタイミングを指します。
このプロセスオントロジーログは、FX市場で取引を実行するプロセスの様々な側面を、構造的かつ論理的にモデル化するために使用することができます。取引実行のプロセスに関連する情報の理解、表現、操作を容易にし、他の種類のオントロジーと組み合わせて、金融市場を包括的に把握するために使用することができます。
結論
結論として、圏論は、金融市場のような複雑なシステムをモデル化し、分析するための強力な枠組みを提供するものです。MQL5で圏論を実装することで、始域と射の関係を構造的かつ論理的に表現することができ、トレーダーが市場をより理解し、ナビゲートできるようになります。この記事では、圏論における圏の公理定義の重要性と、それが始域と射の関係をモデル化するための基礎を提供することを強調しました。また、オントロジーログの概念や、関心のある対象(この場合は金融市場)に論理的な構造を提供するためにどのような使い方ができるのかについても説明しました。全体として、MQL5における圏論の実装は、市場をより深く理解し、より多くの情報に基づいた取引決定をおこないたいトレーダーにとって、貴重なツールとなり得るでしょう。トレーダーは、構造的かつ論理的なアプローチで市場を分析し、通常では発見が困難なパターンや機会を特定することができるようになります。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/11958
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索