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

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

MetaTrader 5テスター | 28 7月 2023, 11:16
234 0
Stephen Njuki
Stephen Njuki

はじめに

前回の記事では、圏論について、多集合、相対集合、添字集合という重要な概念を明らかにし、アルゴリズム取引におけるそれらの重要性を探りました。さて、今回のフォローアップでは、モノイドの概念を紹介します。モノイド(単形)は、要素の集合に対する操作をモデル化するための構造化されたアプローチを提供し、数学とコンピュータサイエンスにおいて不可欠な基盤を形成しています。

定義上、モノイドは次の3つのものの集合です。1)集合、 2)その集合の任意の2つの要素を受け取り、その集合の元でもある要素を常に出力する二項演算、3)集合に属する単位元。前述の二項演算でその集合の他の元とペアになった場合、出力は常にこの単位元がペアになっている他の要素になります。また、この二項演算には結合法則が適用されます。別の言い方をすれば、モノイドとは、あらかじめ定義された規則に従いながら、集合の要素を組み合わせる方法です。モノイドは、データの集約と操作に体系的で柔軟なアプローチを提供します。

正式には、メンバー要素a、b、c、単位元e、二項演算*を持つモノイドMは、次のように定義されます。

M*M-->M;            1


e*a-->a;             2


a*e-->a;             3


a*(b*c)-->(a*b)*c   4

式1は、集合の任意の2つの元の組が、集合の元を出力することを強調しています。式2と式3は、出力が常に二項演算の要素であって同一でないという点で、単位元の重要性を強調しています。そして最後に、式4は二項演算*の結合性を強調しています。


図解と方法

モノイドの応用性を説明するために、トレーダーが取引を実行する前に直面する可能性のある5つの意思決定について考えてみましょう。これらは以下の通りです。

  1. 考慮すべきルックバック期間の長さ
  2. 使用するチャートの時間枠
  3. 使用する適用価格
  4. 選択する指標
  5. そして、この情報をもとに、レンジとトレンドのどちらで取引するか。

それぞれの決断について、次を定義します。

  • 選択可能な値の集合
  •  任意の2つの要素を選択するのに役立つ二項演算:この操作は、1つの選択がおこなわれるまで、別のメソッドによってすべての集合要素に対して繰り返し呼び出されるMQL5メソッドとすることができます。
  • この集合の単位元を示すインデックス:この要素は配列である集合の中にあるので、インデックスになります
  • 単純に2つの要素を選択する理想的な二項演算の選択は、以下のいずれかになります。
  • 2つの要素のうち、小さい方を選ぶ操作
  •  評価される2つの要素のうち大きい方を選ぶ操作
  •  二項演算で2つの要素の平均に最も近い要素を集合から選ぶ演算
  •  二項演算で2つの要素の平均から最も遠い要素を集合から選ぶ演算

ルックバック期間、時間枠、適用価格、指標、シグナル解釈の5つの判断ポイントが考慮されています。別のトレーダーには、別の重要な決定ポイントがあるかもしれません。したがって、これは決定的なステップバイステップのガイドではなく、単にこの記事のために選ばれたものであることを心に留めておくことが重要です。

圏論のモノイドを使ってデータを分類する場合、正確で意味のある結果を得るために、いくつか注意点があります。以下はそのリストです。

1)明確に定義されたモノイド構造を持つ

扱うデータが、定義に従って有効なモノイド構造を形成していることを確認する必要があります。モノイド公理を満たしていること(例えば、単位元を持ち、二項演算のもとで結合性があること)を検証します。ここでは、モノイド構造の定義が甘くなる落とし穴の例を3つ紹介します。

閉包の欠如

モノイドで使われる二項演算が、同じ集合や始域に属する要素にならない場合、閉包は違反となります。例えば、減算を使って自然数に対してモノイドを定義しようとすると、自然数ではない要素に遭遇することになります(例えば、3から5を引くと-2になりますが、これは自然数ではありません)。厳密に言えば、演算は足し算でも引き算でも掛け算でもありません。これは、集合の任意の2つの要素を受け取り、その集合の元である1つの要素を返す、明確に定義されたルールを持つ単純なメソッドです。

非結合性

もう1つの落とし穴は、二項演算が結合性を満たさない場合です。モノイドの要素が結合的に組み合わされないと、あいまいで一貫性のない結果になりかねません。例えば、演算が乗算で要素が行列であるモノイドを考えてみましょう。3つの行列a、b、cがある場合、この演算は結合的ではありません。つまり、(a*b)*c≠a*(b*c)となり、モノイド構造が損なわれます。

単位元の欠如

すべてのモノイドは、二項演算のもとで中立要素として働く単位元を持たなければなりません。モノイドに単位元がない場合、特定の要素で演算をおこなうことが問題になります。例えば、除算を使って実数上のモノイドを定義した場合、ゼロによる除算は未定義なので、単位元は存在しません。

一方、適切なモノイド構造の例を3つ挙げてみます。

整数の加算

加算(+)の二項演算を備えた整数の集合はモノイドを形成します。単位元は0であり、加算は結合的であり、整数集合で閉包します。

ゼロでない有理数の乗算

ゼロでない有理数(分数)の集合は、乗算の二項演算(×)でモノイドを形成します。単位元は1であり、掛け算は連想的で、0以外の有理数に対して閉包します。

文字列の連結

文字列の集合は、連結という二項演算によってモノイドを形成します。単位元は空文字列("")であり、連結は結合的で文字列に対して閉包します。

これらの例は、必要な特性を満たし、分類目的に効果的に使用できる、よく定義されたモノイド構造を示しています。フォーム下部

2)意味論と解釈可能性

モノイド演算の意味論とデータとの関係を理解します。その結果得られた分類が、意図した解釈と一致し、問題領域の文脈で意味をなすかどうかを検討します。この5つの例で説明します。

単語頻度の分類

モノイドを使用して、通話記録から企業の四半期ガイダンスを単語の頻度に基づいて分類しているとします。モノイド演算は単純に単語数を合計することもできますが、結果として得られる分類の解釈可能性は、異なる周波数範囲に割り当てられた意味論に依存するため、慎重に検討する必要があります。例えば、単語の出現頻度が高い文書は、特定のトピックに集中していると解釈でき、特定の単語の出現頻度が低い文書は、より広範で多様なコンテンツを示していると解釈できるかもしれません。したくないことは、総単語数だけに注目し、それを主要なモノイド操作の基準として使うことです。

センチメント分析

テキストのセンチメントを分類するためにモノイドを使うとします。モノイド演算は、個々の単語や文からセンチメントスコアを集約することで、より良いパフォーマンスを発揮する可能性があります。例を考えてみましょう。ある製品のカスタマーレビューがあり、それらを肯定的、中立的、否定的の3つのセンチメントカテゴリに分類したいとします。モノイドアプローチを使用することを決定し、モノイド操作は各レビュー内の個々の文章からセンチメントスコアを集約します。この例では、-1から1までのセンチメントスコアを割り当て、-1は非常に否定的なセンチメント、0は中立的なセンチメント、1は非常に肯定的なセンチメントを表します。モノイド演算は、センチメントスコアの単純な合計をおこないます。では、カスタマーレビューを見てみましょう。

レビュー:「製品はいい。しかし、カスタマーサービスは最低だった。」

これを分類する適切な方法は、個々のセンテンスに分割し、各文にセンチメントスコアを割り当てることでしょう。

文章1:「製品は良い」-センチメントスコア:0.8(肯定的)

文章2:「しかし、カスタマーサービスは最低だった」-センチメントスコア:-0.7(否定的)

次に、レビューの全体的なセンチメントスコアを得るために、モノイド演算を適用します。

総合センチメントスコア= 0.8 + (-0.7) = 0.1

センチメントスコアの範囲に割り当てられた意味論に基づいて、全体のセンチメントスコア0.1をわずかに肯定的なセンチメントと解釈します。したがって、モノイド分類に基づき、このレビューを「中立」または「やや肯定的」に分類します。このとき避けたいのは、「良い」という単語があるからといって、両方の文章を1つの文章とみなし、逸話的な点数をつけることです。詳細を検討するのは良い習慣でしょう。

画像の分類

色、テクスチャ、形などの視覚的特徴に基づいて画像を分類するモノイドは、これらの特徴を組み合わせることを必要とし、分類の結果となるこれらの組み合わせられた特徴の解釈可能性は、意図された、あるいは意味のあるカテゴリにどのようにマッピングするかに依存します。異なる特徴の組み合わせに割り当てられる意味論は、分類結果の理解の仕方に大きく影響します。

モノイドを使った画像分類における意味論と解釈可能性の重要性を示すために、この図を考えてみましょう。モノイドアプローチを使って画像を2つのカテゴリに分類するとします。視覚的特徴に基づく「犬」と「猫」(トレーダーにとっては、これを強気と弱気のヘッドアンドショルダーパターンと言い換えてもよいですが、原理は同じです)です。モノイド演算は、画像から読み取った色とテクスチャの特徴を組み合わせることになります。ここでは、2つの主要な視覚的特徴があると仮定します。「毛皮の色」と「テクスチャーの複雑さ」です。 毛皮の色は「Light(明るい)」か「Dark(暗い)」のどちらかに分類され、テクスチャーの複雑さは「Simple(単純)」か「Complex(複雑)」のどちらかに分類されます。 では、2つの画像を考えてみましょう。

画像1はシンプルな毛並みの白猫です。

  • 毛皮の色:Light
  • テクスチャーの複雑さ:Simple
  • 画像2は複雑な毛並みを持つ黒い犬です。これは次を暗示します。
  • 毛皮の色:Dark
  • テクスチャーの複雑さ:Complex

モノイドアプローチを使ってこれらの画像を分類するには、モノイド演算(連結、和算など)に従って視覚的特徴を組み合わせます。

画像1の場合:「Light」+「Simple」=「LightSimple」

画像2の場合:「Dark」+「Complex」=「DarkComplex」

さて、ここで重要なのは意味論と解釈可能性です。意味のあるカテゴリにマッピングするためには、組み合わされた特徴に意味を割り当てる必要があります。私たちの場合、単純すぎる例を使っているので:

「LightSimple」は「Cat」のカテゴリと解釈できます。なぜなら、明るい毛色とシンプルな質感は猫に共通する特徴だからです。

暗い毛皮の色と複雑な質感が犬を連想させることが多いため、「DarkComplex」は「Dog」のカテゴリと解釈されます。

組み合わされた特徴に適切な意味と解釈を割り当てることで、画像1を「猫」、画像2を「犬」と正しく分類することができます。

顧客セグメンテーション

モノイドを使って、購買行動に基づいて顧客をセグメント化しているとします。モノイド操作には、取引データや顧客属性の集約が含まれるかもしれません。しかし、結果として得られるセグメントの解釈可能性は、これらのセグメントをどのように解釈し、ラベル付けするかにかかっています。例えば、意味論と始域知識に基づいて、「高価値顧客」や「解約しやすい顧客」といったラベルを割り当てることができます。

時系列分類

株式市場の動向など、時系列データを分類するためにモノイドを使用することを検討します。モノイド操作には、価格、出来高、ボラティリティといったさまざまな特徴を組み合わせることが含まれます。しかし、分類の解釈可能性は、市場の状況との関連において、結果として得られる組み合わせの意味論をどのように定義するかにかかっています。異なる解釈は、明確な洞察と意思決定への影響につながります。

これらすべての例において、モノイド演算に割り当てられた意味論と、その結果としての分類は、意味のある解釈と意思決定にとって極めて重要です。これらの意味論を注意深く考慮することで、結果として得られる分類が望ましい解釈と一致し、データの効果的な分析と理解を可能にします。

3)データの前処理

これは、モノイドで分類する前のデータの品質管理上重要です。モノイド構造との互換性を確保するために、データを前処理するのが適切でしょう。例えば、すべての演算関数の出力は、モノイド集合の明確な元であるべきであり、丸め誤差が生じる可能性のある複数の小数点を持つ浮動データであってはなりません。これを実現するには、データを正規化(正則化)したり、モノイド演算に適した形式に変換したりします。しかし、取引システム全体の一貫性を高めるためには、欠損値の処理にも対処する必要があります。

4)データの均質性

分類するデータが、各カテゴリ内で一定の均質性を持っていることを確認します。例えば、指標を選択する段階で、使用するセットモノイドは、一貫性のある比較可能な値や重み付けを持つ両方の指標を持つべきです。RSIオシレーターとボリンジャーバンドを使用していることを考えると、これは明らかにデフォルトではありません。ただし、両者が比較可能で均質であることを保証するために、片方を正規化します。モノイドは、各クラス内で類似した特性を示すデータに適用した場合に、最も効果的に機能します。

5)カテゴリのカーディナリティ

モノイドを使って形成できる別個のカテゴリのカーディナリティを考えてみましょう。結果として得られるカテゴリの数が多すぎたり少なすぎたりすると、分類の有用性や結果の解釈可能性に影響を与える可能性があります。

カテゴリのデータのカーディナリティの影響を例で説明します。

カレンダーのニュースイベントのセンチメントに基づいてFXペアの方向を予測する分類タスクに取り組んでいて、「期待値を上回る」、「期待値と一致する」、「期待値を下回る」という 3 つの値を取ることができるターゲット変数「Sentiment」を含むデータセットがあるとします。

次はデータセットの例です。

レビュー センチメント
連銀製造業生産前月比
GDT価格指数 普通
企業在庫前月比
NAHB住宅市場指数(NAHBHousingMarketIndex)

この例では、Sentiment変数に良、普通、悪の3つのカテゴリがあることがわかります。 カーディナリティとは、変数に含まれる明確なカテゴリの数のことです。

このデータセットにおけるSentiment変数のカーディナリティは3です。

カテゴリのデータのカーディナリティは、分類タスクに影響を与える可能性があります。2つのシナリオを考えてみましょう。

シナリオ1:

ここでは、データのカーディナリティのバランスが悪いため、「良」センチメントカテゴリは、「普通」および「悪」カテゴリと比較して、サンプル数が著しく多いというバランスの悪いデータセットになっています。例えば、サンプルの80%が「良」、10%が「同」、10%が「悪」と表示されているとします。

このシナリオでは、データのカーディナリティのバランスの悪さが分類モデルのバイアスにつながる可能性があります。モデルは、多数派クラス(「上」)をより頻繁に予測する方に偏り、少数派クラス(「普通」「下」)を正確に予測しにくいかもしれません。この結果、少数クラスの精度とリコールが低くなり、分類モデルの全体的な性能と解釈可能性に影響を与える可能性があります。

シナリオ2:

この場合、上記のようにモノイド集合の要素に文字列値を帰属させるのではなく、モノイド集合の各要素をより正確に重み付けして記述するために、浮動小数点データを使用します。これはまた、シナリオ1のように、各セット要素に対して可能な重み/値の数が決まっていないことを意味します。

6)拡張性

特に大規模なデータセットを扱う場合には、モノイド分類のスケーラビリティを評価する必要があるでしょう。計算の複雑さによっては、大量のデータを効率的に扱うための代替技術や最適化を検討する必要があるかもしれません。大規模なデータセットを扱う場合の1つのアプローチは、特徴エンジニアリングをおこなうことです。モノイド同型性は、特徴工学において様々なタスクに利用できます。その1つが上場企業の評価です。

この同型性によって、入力特徴を、評価モデルにとってより鋭い予測力を持つ新しい特徴空間に変換することができます。例えば、上場企業の収益、利益、資産、負債など様々な財務指標を含むデータセットがある場合を考えてみましょう。

1つの一般的なアプローチは、評価モデルで広く使用されている主要な財務比率を導き出し、それに焦点を当てるためにモノイド同型を使用することです。例えば、収益モノイド集合を、収益成長率を表す新しい正規化モノイド集合にマップする同型性を定義することができます。この変換により、データ要件が明らかに削減され、モノイドがよりスケーラブルになります。なぜなら、収益モノイドセットに独立してリストされている多くの企業が、コード始域で同じ収益成長率の値を共有するからです。

同様に、モノイドの同型性を使って、収益モノイドを一株当たり利益(EPS)を表すモノイドにマッピングすることができます。EPSは、企業の収益性を1株当たりで示す評価指標として広く使われています。その他にも、モノイド分類モデルをスケーラブルに保つという同じ目標に向かって、役に立つ重要な比率がたくさんあります。

その一方で、クラスタ内の複数のノードで並列にデータを処理するために、ApacheHadoopやApacheSparkなどの分散コンピューティングフレームワークへの依存を最小限に抑えたいのです。これらのアプローチによって、作業負荷を分散し、処理時間を短縮することが可能になり、大規模なデータセットを扱うことができるようになります。「下流」というのは、解決しようとしているすべての問題は、そもそもモノイド設計のレベルでもっと機転を利かせて処理できたはずだからです。

7)一般化可能性

新しい未見のデータを使って、分類結果の一般化可能性を評価しなければならないでしょう。モノイドベースの分類法は、異なるデータセットやコンテキストにまたがって、信頼性が高く一貫性のある分類を提供しなければなりません。

ローン申込者の信用度を予測するためのモノイドベースの分類を開発しているとします。データセットには、収入、クレジットスコア、負債比率、職歴など、過去のローンデータが含まれます。

最終的な分類結果の一般化可能性を評価するためには、新しい未見のデータに対してモデルがどの程度うまく機能するかを評価する必要があります。例えば、特定の地域や期間のデータセットでモデルを学習させた後、別の地域や期間のデータセットでテストします。このモデルが、このデータセットや他の多様なデータセットにわたって一貫した信頼性の高い分類性能を示せば、一般化可能性が高いことを示すでしょう。

モノイドベースの分類手法で汎化性を達成する上で、潜在的な落とし穴としては、過学習、データバイアス、特徴選択バイアスなどがあります。説明すると、過学習とは、(例えば)モノイド演算関数が訓練データに特化しすぎて、新しいデータでのパフォーマンスが低下することです。逆に、訓練データセットがより広範な母集団を代表するものでない場合、データの偏りが起こり、偏った分類につながる可能性があります。特徴選択バイアスでは、与えられたコンテキストに関連する情報を捕捉していない特徴の選択は、モデルの一般化可能性に影響を与えます。

8)評価指標

モノイド分類の品質と有効性を評価するための適切な評価指標を定義します。これらの指標は、精度、正確さ、リコール、F1スコアなどの要素を考慮し、特定の問題と目標に沿ったものでなければなりません。

9)過学習と学習不足

モノイド分類モデルの過学習と学習不足を防ぎます。これらの問題を防ぎ、モデルの汎化を促進するために、交差検証、正則化、早期停止などのテクニックを適用します。

10)解釈可能性と説明可能性

得られた分類の解釈可能性と説明可能性を検討します。モノイドは強力な分類能力を提供できますが、分類決定がどのようになされるかを理解し、それを意味のある方法で説明できるようにすることが重要です。


実装

ルックバック期間

利用可能なルックバック期間のオプションを表すために、1から8までの8つの整数からなるモノイド領域が使われます。次節で説明するように、分析時間枠は異なるが、1期間単位は4とし、テスト時間枠は1時間に固定します。それぞれの新しいバーでピリオドを選択する必要があります。それぞれの測定単位は、同じ長さの過去の期間と比較した場合の、その期間における移動の相対的な大きさの割合となります。例えば、期間1(4本のバー)のポイントの価格変動がAで、その前の期間の価格変動がBであったとすると、期間1の重みまたは値は以下の式で与えられます。

= ABS(A)/(ABS(A) + ABS(B))

ABS()関数は絶対値を表します。この式は、分母の最小値が、検討中の証券の大きさの少なくとも1ポイント以上であることを確認することにより、ゼロ除算をチェックします。

モノイド演算と単位元は、冒頭の方法に従った最適化から選択されます。

時間枠

時間枠を表すモノイドは8つの時間枠を持ちます。

  • PERIOD_H1
  • PERIOD_H2
  • PERIOD_H3
  • PERIOD_H4
  • PERIOD_H6
  • PERIOD_H8
  • PERIOD_H12
  • PERIOD_D1

これらの時間枠の重み付けと値付けは、上記のルックバック期間と同じパターンに従います。つまり、それぞれのタイムフレームにおける以前の価格バーの変化に基づいて、終値の変化率を比較します。

適用価格

適用価格セットモノイドは、4つの適用価格から選択できます。

  • MODE_MEDIAN((高値+低値)/2)
  • MODE_TYPICAL((高値+安値+終値)/3)
  • MODE_OPEN
  • MODE_CLOSE

ここでの重み付けと値の割り振りは、上記のものとは異なります。この場合、ルックバック期間で選択した期間における各適用価格の標準偏差を使用して、各適用価格の重み付けまたは値を決定します。操作とIDインデックスの選択は上記と同じです。

指標

指標を選択するためのモノイドは2つの選択肢しかありません。

  • RSIオシレーター
  • ボリンジャーバンドエンベロープ

RSIオシレーターは0~100の範囲で正規化されていますが、ボリンジャーバンド指標は正規化されていないだけでなく、複数のバッファストリームを備えています。ボリンジャーバンドを正規化し、RSIオシレーターと比較できるようにするために、現在の価格と基準バンドCの差を取り、それを上限バンドと下限バンドのギャップの大きさDで割ります。したがって、バンドの値はまず次のようになります。

= C/(ABS(C) + ABS(D))

以前と同様、この値はゼロ除算でチェックされます。ただし、この値はマイナスになることもあり、10進数で1.0になる傾向があります。これらの2つの側面を正規化し、RSIのように0~100の範囲に収めるために、常にプラスになるように1.0を加え、その合計に50.0を掛けて0~100の範囲に収めます。そこで、RSIとボリンジャーバンドの両方について、0~100の範囲の値で重み付けをおこない、演算子関数と要素インデックスを上記の方法で述べたように選択します。

決定

この最後のモノイドについても、私たちのセットは2つの選択肢しか持ちません。これらは以下の通りです。

  • トレンドに乗る
  • レンジ取引

この2つの要素を定量化するために、選択したルックバック期間において、期間終了時に最終的なトレンドに反して引き戻された価格ポイントの量を、この期間の総価格レンジに対する割合として考えます。0.0~1.0の小数で指定します。これは、レンジでの取引の重みを直接測るもので、トレンドでの取引は1.0からこの値を引いたものになります。操作方法とインデックスの選択は上記の通りです。

このようにして、内蔵のEAトレーリングクラスのインスタンスとしてモノイド決定を実装します。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CTrailingCT::Operate_8(CMonoid<double> &M,EOperations &O,double &Values[],int &InputIndices[],int &OutputIndices[])
   {
      for(int i=0;i<8;i++)
      {
         m_element.Let();
         if(m_lookback.Get(i,m_element))
         {
            if(!m_element.Get(0,Values[InputIndices[i]]))
            {
               printf(__FUNCSIG__+" Failed to get double for 1 at: "+IntegerToString(i+1));
            }
         }
         else{ printf(__FUNCSIG__+" Failed to get element for 1 at: "+IntegerToString(i+1)); }
      }
      
      //
      
      if(O==OP_LEAST)
      {
         for(int i=0;i<8;i+=2)
         {
            if(Values[InputIndices[i]]<Values[InputIndices[i+1]]){ OutputIndices[i/2]=i; }
            else if(Values[InputIndices[i]]>Values[InputIndices[i+1]]){ OutputIndices[i/2]=i+1; }
            else { OutputIndices[i/2]=m_lookback.Identity(); }
         }
      }
      else if(O==OP_MOST)
      {
         for(int i=0;i<8;i+=2)
         {
            if(Values[InputIndices[i]]>Values[InputIndices[i+1]]){ OutputIndices[i/2]=i; }
            else if(Values[InputIndices[i]]<Values[InputIndices[i+1]]){ OutputIndices[i/2]=i+1; }
            else { OutputIndices[i/2]=m_lookback.Identity(); }
         }
      }
      else if(O==OP_CLOSEST)
      {
         for(int i=0;i<8;i+=2)
         {
            int _index=-1;
            double _mean=0.5*(Values[InputIndices[i]]+Values[InputIndices[i+1]]),_gap=DBL_MAX;
            for(int ii=0;ii<8;ii++)
            {
               if(_gap>fabs(_mean-Values[InputIndices[ii]])){ _gap=fabs(_mean-Values[InputIndices[ii]]); _index=ii;}
            }
            //
            if(_index==-1){ _index=m_lookback.Identity(); }
            
            OutputIndices[i/2]=_index;
         }
      }
      else if(O==OP_FURTHEST)
      {
         for(int i=0;i<8;i+=2)
         {
            int _index=-1;
            double _mean=0.5*(Values[InputIndices[i]]+Values[InputIndices[i+1]]),_gap=0.0;
            for(int ii=0;ii<8;ii++)
            {
               if(_gap<fabs(_mean-Values[InputIndices[ii]])){ _gap=fabs(_mean-Values[InputIndices[ii]]); _index=ii;}
            }
            //
            if(_index==-1){ _index=m_lookback.Identity(); }
            
            OutputIndices[i/2]=_index;
         }
      }
   }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CTrailingCT::Operate_4(CMonoid<double> &M,EOperations &O,double &Values[],int &InputIndices[],int &OutputIndices[])
   {
      for(int i=0;i<4;i++)
      {
         m_element.Let();
         if(m_lookback.Get(i,m_element))
         {
            /*printf(__FUNCSIG__+
               " values size: "+IntegerToString(ArraySize(Values))+
               " in indices size: "+IntegerToString(ArraySize(InputIndices))+
               " in indices index: "+IntegerToString(InputIndices[i])
               );*/
               
            if(!m_element.Get(0,Values[InputIndices[i]]))
            {
               printf(__FUNCSIG__+" Failed to get double for 1 at: "+IntegerToString(i+1));
            }
         }
         else{ printf(__FUNCSIG__+" Failed to get element for 1 at: "+IntegerToString(i+1)); }
      }
      
      //
      
      if(O==OP_LEAST)
      {
         for(int i=0;i<4;i+=2)
         {
            if(Values[InputIndices[i]]<Values[InputIndices[i+1]]){ OutputIndices[i/2]=i; }
            else if(Values[InputIndices[i]]>Values[InputIndices[i+1]]){ OutputIndices[i/2]=i+1; }
            else { OutputIndices[i/2]=m_lookback.Identity(); }
         }
      }
      else if(O==OP_MOST)
      {
         for(int i=0;i<4;i+=2)
         {
            if(Values[InputIndices[i]]>Values[InputIndices[i+1]]){ OutputIndices[i/2]=i; }
            else if(Values[InputIndices[i]]<Values[InputIndices[i+1]]){ OutputIndices[i/2]=i+1; }
            else { OutputIndices[i/2]=m_lookback.Identity(); }
         }
      }
      else if(O==OP_CLOSEST)
      {
         for(int i=0;i<4;i+=2)
         {
            int _index=-1;
            double _mean=0.5*(Values[InputIndices[i]]+Values[InputIndices[i+1]]),_gap=DBL_MAX;
            for(int ii=0;ii<4;ii++)
            {
               if(_gap>fabs(_mean-Values[InputIndices[ii]])){ _gap=fabs(_mean-Values[InputIndices[ii]]); _index=ii;}
            }
            //
            if(_index==-1){ _index=m_lookback.Identity(); }
            
            OutputIndices[i/2]=_index;
         }
      }
      else if(O==OP_FURTHEST)
      {
         for(int i=0;i<4;i+=2)
         {
            int _index=-1;
            double _mean=0.5*(Values[InputIndices[i]]+Values[InputIndices[i+1]]),_gap=0.0;
            for(int ii=0;ii<4;ii++)
            {
               if(_gap<fabs(_mean-Values[InputIndices[ii]])){ _gap=fabs(_mean-Values[InputIndices[ii]]); _index=ii;}
            }
            //
            if(_index==-1){ _index=m_lookback.Identity(); }
            
            OutputIndices[i/2]=_index;
         }
      }
   }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CTrailingCT::Operate_2(CMonoid<double> &M,EOperations &O,double &Values[],int &InputIndices[],int &OutputIndices[])
   {
      for(int i=0;i<2;i++)
      {
         m_element.Let();
         if(m_lookback.Get(i,m_element))
         {
            if(!m_element.Get(0,Values[InputIndices[i]]))
            {
               printf(__FUNCSIG__+" Failed to get double for 1 at: "+IntegerToString(i+1));
            }
         }
         else{ printf(__FUNCSIG__+" Failed to get element for 1 at: "+IntegerToString(i+1)); }
      }
      
      //
      
      if(m_lookback_operation==OP_LEAST)
      {
         for(int i=0;i<2;i+=2)
         {
            if(Values[InputIndices[i]]<Values[InputIndices[i+1]]){ OutputIndices[0]=i; }
            else if(Values[InputIndices[i]]>Values[InputIndices[i+1]]){ OutputIndices[0]=i+1; }
            else { OutputIndices[0]=m_lookback.Identity(); }
         }
      }
      else if(m_lookback_operation==OP_MOST)
      {
         for(int i=0;i<2;i+=2)
         {
            if(Values[InputIndices[i]]>Values[InputIndices[i+1]]){ OutputIndices[0]=i; }
            else if(Values[InputIndices[i]]<Values[InputIndices[i+1]]){ OutputIndices[0]=i+1; }
            else { OutputIndices[0]=m_lookback.Identity(); }
         }
      }
      else if(m_lookback_operation==OP_CLOSEST)
      {
         for(int i=0;i<2;i+=2)
         {
            int _index=-1;
            double _mean=0.5*(Values[InputIndices[i]]+Values[InputIndices[i+1]]),_gap=DBL_MAX;
            for(int ii=0;ii<2;ii++)
            {
               if(_gap>fabs(_mean-Values[InputIndices[ii]])){ _gap=fabs(_mean-Values[InputIndices[ii]]); _index=ii;}
            }
            //
            if(_index==-1){ _index=m_lookback.Identity(); }
            
            OutputIndices[0]=_index;
         }
      }
      else if(m_lookback_operation==OP_FURTHEST)
      {
         for(int i=0;i<2;i+=2)
         {
            int _index=-1;
            double _mean=0.5*(Values[InputIndices[i]]+Values[InputIndices[i+1]]),_gap=0.0;
            for(int ii=0;ii<2;ii++)
            {
               if(_gap<fabs(_mean-Values[InputIndices[ii]])){ _gap=fabs(_mean-Values[InputIndices[ii]]); _index=ii;}
            }
            //
            if(_index==-1){ _index=m_lookback.Identity(); }
            
            OutputIndices[0]=_index;
         }
      }
   }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CTrailingCT::GetLookback()
   {
      m_close.Refresh(-1);
      
      int _x=StartIndex();
      
      for(int i=0;i<8;i++)
      {
         int _period=(__LOOKBACKS[i]*PeriodSeconds(PERIOD_H4))/PeriodSeconds(m_period);
         double _value=fabs(m_close.GetData(_x)-m_close.GetData(_x+_period))/(fabs(m_close.GetData(_x)-m_close.GetData(_x+_period))+fabs(m_close.GetData(_x+_period)-m_close.GetData(_x+_period+_period)));
         
         m_element.Let();
         m_element.Cardinality(1);
         if(m_element.Set(0,_value))
         {
            ResetLastError();
            if(!m_lookback.Set(i,m_element,true))
            {
               printf(__FUNCSIG__+" Failed to assign element at index: "+IntegerToString(i)+", for lookback. ERR: "+IntegerToString(GetLastError()));
            }
         }
      }
      
      //r of 8
      double _v1[8];ArrayInitialize(_v1,0.0);
      int _i1_in[8];for(int i=0;i<8;i++){ _i1_in[i]=i; }
      int _i1_out[4];ArrayInitialize(_i1_out,-1);
      Operate_8(m_lookback,m_lookback_operation,_v1,_i1_in,_i1_out);
      
      
      //r of 4
      double _v2[8];ArrayInitialize(_v2,0.0);
      int _i2_out[2];ArrayInitialize(_i2_out,-1);
      Operate_4(m_lookback,m_lookback_operation,_v2,_i1_out,_i2_out);
      
      
      //r of 2
      double _v3[8];ArrayInitialize(_v3,0.0);
      int _i3_out[1];ArrayInitialize(_i3_out,-1);
      Operate_2(m_lookback,m_lookback_operation,_v2,_i2_out,_i3_out);
      
      return(4*__LOOKBACKS[_i3_out[0]]);
   }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
ENUM_TIMEFRAMES CTrailingCT::GetTimeframe(void)
   {
      for(int i=0;i<8;i++)
      {
         ResetLastError();
         double _value=0.0;
         double _buffer[];ArrayResize(_buffer,3);ArrayInitialize(_buffer,0.0);ArraySetAsSeries(_buffer,true);
         if(CopyClose(m_symbol.Name(),__TIMEFRAMES[i],0,3,_buffer)>=3)
         {
            _value=fabs(_buffer[0]-_buffer[1])/(fabs(_buffer[0]-_buffer[1])+fabs(_buffer[1]-_buffer[2]));
         }
         else{ printf(__FUNCSIG__+" Failed to copy: "+EnumToString(__TIMEFRAMES[i])+" close prices. err: "+IntegerToString(GetLastError())); }
         
         m_element.Let();
         m_element.Cardinality(1);
         if(m_element.Set(0,_value))
         {
            ResetLastError();
            if(!m_timeframe.Set(i,m_element,true))
            {
               printf(__FUNCSIG__+" Failed to assign element at index: "+IntegerToString(i)+", for lookback. ERR: "+IntegerToString(GetLastError()));
            }
         }
      }
      
      //r of 8
      double _v1[8];ArrayInitialize(_v1,0.0);
      int _i1_in[8];for(int i=0;i<8;i++){ _i1_in[i]=i; }
      int _i1_out[4];ArrayInitialize(_i1_out,-1);
      Operate_8(m_timeframe,m_timeframe_operation,_v1,_i1_in,_i1_out);
      
      
      //r of 4
      double _v2[8];ArrayInitialize(_v2,0.0);
      int _i2_out[2];ArrayInitialize(_i2_out,-1);
      Operate_4(m_timeframe,m_timeframe_operation,_v2,_i1_out,_i2_out);
      
      
      //r of 2
      double _v3[8];ArrayInitialize(_v3,0.0);
      int _i3_out[1];ArrayInitialize(_i3_out,-1);
      Operate_2(m_timeframe,m_timeframe_operation,_v2,_i2_out,_i3_out);
      
      return(__TIMEFRAMES[_i3_out[0]]);
   }


つまり、Operate_8関数は、モノイド集合の8つの要素をペアにして、それぞれのペアから1つずつ、合計4つの要素を選択します。同様に、Operate_4関数は、Operate_8から取得した4つの要素をペアにして、それぞれのペアから1つずつ、さらに2つの要素を選択します。最後に、Operate_2関数は、Operate_4から取得した2つの要素をペアにして、勝ちの要素を選択します。


EAシグナルクラスの内蔵RSIシグナルとエキスパートマネークラスの固定資金管理を使用するエキスパートアドバイザー(EA)の一部として、ポジションの理想的なトレーリングストップを決定するためにこのシステムでテストを実行すると、以下のレポートが得られます。

r_1

対照として、非常によく似たEAで同様の操作を行ったところ、当社との唯一の違いは、移動平均に基づく内蔵トレーリングストップであるトレーリングシステムであり、以下のレポートが得られました。

r_2


結論

モノイドをデータ分類の手段として、つまり意思決定ブロックとして見てきました。一般化可能で曲線あてはめされない、整ったモノイドを持つことの重要性が注目されています。また、内蔵のEAトレーリングクラスのインスタンスとして機能することで、ストップロスポジションを調整するこのシステムの実装の可能性についても検討しました。


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

添付されたファイル |
ct_8.mqh (64.34 KB)
TrailingCT8.mqh (35.04 KB)
リプレイシステムの開発—市場シミュレーション(第3回):設定の調整(I) リプレイシステムの開発—市場シミュレーション(第3回):設定の調整(I)
まずは現状を明らかにすることから始めましょう。今やらなければ、すぐに問題になります。
リプレイシステムの開発—市場シミュレーション(第2回):最初の実験(II) リプレイシステムの開発—市場シミュレーション(第2回):最初の実験(II)
今回は、1分という目標を達成するために、別の方法を試してみましょう。ただし、このタスクは思っているほど単純ではありません。
データサイエンスと機械学習(第14回):コホネンマップを使って市場で自分の道を見つける データサイエンスと機械学習(第14回):コホネンマップを使って市場で自分の道を見つける
複雑で変化し続ける市場をナビゲートする、最先端の取引アプローチをお探しですか。人工ニューラルネットワークの革新的な形態であるコホネンマップは、市場データの隠れたパターンやトレンドを発見するのに役立ちます。この記事では、コホネンマップがどのように機能するのか、そして、より賢く、より効果的な取引戦略を開発するために、どのように活用できるのかを探ります。経験豊富なトレーダーも、これから取引を始める人も、このエキサイティングな新しいアプローチを見逃す手はありません。
リプレイシステムの開発—市場シミュレーション(第1回):最初の実験(I) リプレイシステムの開発—市場シミュレーション(第1回):最初の実験(I)
市場がしまっているときに研究したり、市場の状況をシミュレーションしたりできるシステムを作成してはどうでしょうか。ここで、このトピックを扱う新しい連載を開始します。