English Русский Español Deutsch Português
preview
MQL5の圏論(第23回):二重指数移動平均の別の見方

MQL5の圏論(第23回):二重指数移動平均の別の見方

MetaTrader 5トレーディングシステム | 29 1月 2024, 11:27
143 0
Stephen Njuki
Stephen Njuki

はじめに

MQL5で圏論をすぐに実装するのは大変な努力のように思えます。このテーマに関しては、読みやすいものやセルフサポート的なものがあまりありません。世の中にはたくさんの本がありますが、それらは修士課程や博士課程の学生を対象としたものであり、また学術的な場において構成されていることが多いです。つまり、このトピックをより身近なものにしようとする試みは、決して容易なものではありません。学術的な議論や理論を唱えることが目的なのではなく、その教訓を解釈し、トレーダーに適用することが目的であるからです。そのために、シンプルで日常的な指標を違った角度から見るというテーマを広げています。

本稿の目的は、自然変換の水平合成の概念を強調することです。前回の記事では、その反意語について考えました。

そこでは、2つの圏間の3つの関手が、垂直合成における2つの自然変換を意味することが、圏が価格の時系列と同じ価格の移動平均時系列のような単純なデータセットである場合に、どのように推測されるかを見ました。今回は、二重指数移動平均としてよく知られている移動平均の第3の圏を追加することによって、移動平均時系列を水平方向に拡張します。このよく知られた指標の私たちのバリエーションは、文字通り確立された公式を使用するのではなく、自分たちの目的のために、単に移動平均の移動平均であることによって移動平均を平滑化します。関手の関係は前回の記事と似ていますが、圏間の関手は前回の 3 つではなく2つしかありません。しかし、前回の記事のように、2つの圏間の各関手は、それぞれの移動平均期間を持ち、各関手のペア間の自然変換は、分析のための時系列バッファの形成に役立ちます。

取引においてボラティリティを予測することの重要性は、そもそもどのようなポジションを持つべきか(ロングかショートか)を判断することほど重要ではないかもしれません。とはいえ、他の既存のエントリシグナル戦略の潜在的な利用法や改良点、あるいはそのアイデアを利用した新しい戦略の可能性を検討する機会を与えてくれます。これは過去の記事で何度も考察してきたことであり、ここで再演することは異質なことではありません。ボラティリティの予測は、EAトレーリングクラスのインスタンスで処理されます。読者の皆さんは、このクラスを他のシグナルや自分だけの戦略でテストして、自分にとって最適なものを見つけてください。この記事では、素晴らしいオシレーターに注目していきます。


背景

圏論の概念を簡単におさらいすると、圏としての順序、関手、特殊な状況における関手としての射、そして最後に自然変換の存在を証明する自然性の資格形について見てきました。これらに至るまでには、もっと前に見た概念もありますが、このリストで、この記事のために私たちが解き明かしていることの基礎をカバーすることができるでしょう。

この記事で自然変換の「水平」合成が意味するものを理解することは極めて重要です。なぜなら、私たちは単に2次元の自然変換を図式的に表現することを指しているのではありません。この図を考えてみましょう。

ここでいう水平性とは、関手が連鎖して配置され、異なるペア(またはセット)が異なる始域と終域の圏にリンクしていることを意味します。例えば、最初の2つの関手AとCは圏1と圏2にリンクし、関手BとDは圏2と圏3にリンクしています。これは、すべての関手が同じ圏のペアをリンクしていた前回の記事とは対照的です。つまり、前回の記事で2つの圏が縦に描かれていたとしても、自然な変形が横(水平)に走っていたとしても、それは縦構図であることに変わりはありません。同じように、たとえ圏が縦に並んで描かれていたとしても、つまり自然変換が「縦」の順番に並んでいるように見えたとしても、それは水平合成とみなされます。

そこで、価格時系列、移動平均時系列、二重指数移動平均時系列を対象とします。二重指数時系列の使用は、この圏と価格時系列の圏を比較する際に、若干の混乱を引き起こす可能性があります。事実上、3番目の圏は圏1の二重指数移動平均です。もし関手BとDを、圏2のオブジェクトに対する二重指数移動平均と単純に考えていたとしたら、その結果はあまり有益ではなかったでしょう。つまり、MAのDEMAとは何なのでしょう。これらの関手は、元の価格系列である圏1から写像されているように見えますが、これは単に圏2からの移動平均の写像であり、事実上そうなっているのです。この記事の最後に完全なソースコードが添付されているので、読者はこれを修正して独自のテストをおこなうことができますが、この区別は頭に入れておくことが重要です。


圏と関手

最初の圏、圏1には価格時系列があります。以前の記事で全順序としてどのように解釈されるかを検証しているので、ご参照ください。ここでの時系列は、以前の記事で示したように線形順序によく似ています。この圏に含まれるオブジェクトは、各インターバルにおける価格と時刻の値です。つまり、各オブジェクトは少なくとも2つの要素、価格と時刻の値を持つことになります。圏の射は、シリーズの時間間隔によって定義され、単純に価格値を時間の昇順に連鎖させます。この記事で検証する時系列はEURUSDです。

価格の移動平均は圏2となり、最初の圏と同様、線形順序として見ることができる時系列であり、したがって圏でもあります。主に2つのオブジェクトが登場しますが、それぞれは時系列です。1 つ目は関手Aによって写像され、関手Aによって定義された期間における価格系列の移動平均を表します。このモデルには基本的に2つの移動平均期間があり、関手Aによって使用される移動平均期間は2つのうちの短い方になります。Cとラベル付けされた2番目の関手も、価格時系列から圏2に写像され、関手Aよりも長い移動平均期間を持つことになります。

最後の圏である圏3は、4つの時系列オブジェクトを持ちますが、2つのみを考察します。オブジェクトの各ペア(4つのオブジェクトは2ペア)は、移動平均の移動平均を計算して二重指数移動平均(DEMA)を導き出す1つの関手によって写像されます。上記で明らかにしたように、コードからDEMAは価格系列の圏1に写像されているように見えますが、これは計算を簡単にするためであり、カスタム指標を考え出す必要はありません。移動平均の移動平均なので、圏2からの写像とみなすことができます。現在、圏2には2つの時系列の移動平均線があり、これらは高速移動平均線と低速移動平均線と見なすことができます。しかしこれは、圏2からの2つの関手がそれぞれ2つのオブジェクトに写像され、特定のオブジェクトや時系列にリンクされるため、圏3が4つのオブジェクトを持つことを意味します。


関手と自然変換

この2つの関手ペアによる自然変換の合計は6になりますが、圏3の4つのオブジェクトのうち2つしか使わないので、その数は2になります。要約すると、自然変換とは関手の2つの終域オブジェクトの差です。したがって、2つの移動平均オブジェクトを差し引くことで、これを簡単に捉えることができます。前回の記事で説明したように、各自然変換は時系列バッファとして捕捉され、これらのバッファの相関関係は、オープンポジションのトレーリングストップを調整する際の判断材料となります。しかし、前回の記事では、これらの自然変換バッファの間のこの相関関係は、ホワイトノイズフィルタとして機能していました。今回は、トレーリングストップの調整にのみ関心があるため、移動平均のトレンドの方向にストップロスの調整を適用する必要がある場合は、再びフィルタとして機能します。したがって、これらのバッファの間に正の相関関係がある負トレンドは、ショートポジションのストップロスを下げる青信号となります。同様に、十分な相関関係(私たちの閾値はゼロ以上である)を持つ正のトレンドは、ロングポジションが存在する場合、ストップロスを上げることを示します。そのため、負の相関関係によって「シグナルがない」ときや、私たちのストップムーブメントの判断はポジションごとにおこなわれるため、オープンしたポジションのタイプがシグナルに適合しないときが多々あります。したがって、前回の記事で見たように、このモデルはEAシグナルクラスのインスタンスとして再構成され、ウィザードを通じてEAに組み入れることができます。


ボラティリティの予測

従って、ボラティリティを予測することが、この連載で何度か取り上げてきたように、EAトレーリングクラスのインスタンスがおこなっていることなのです。移動平均のトレンドは、自然変換バッファ間の実質的な相関によって確認される必要があります。しかし、よりボラティリティを重視した別のアプローチとして、圏1を一連の価格帯(高値から安値を引いた値)として設定することもできたでしょう。この系列は圏2に平均値を持ち、圏3は添付コードで終値でおこなっているようにDEMAとなります。プロセスはほとんど同じですが、このアプローチによって、ボラティリティに対してより敏感な結果が得られる可能性があります。


MQL5での実践的な実装

このモデルのMQL5環境を設定するのは、この連載で見てきたこととよく似ています。繰り返しになりますが、EAトレーリングクラスのインスタンスはMQL5ウィザードのEAの一部として組み立てられ、それを動作させたりテストしたりするためには、ウィザード内でまずEAシグナルクラスのインスタンスが選択されている必要があります。この記事では、内蔵の素晴らしいオシレーターシグナルクラスを使用しました。ここでいう「内蔵」とは、IDEに用意されているシグナルのライブラリにすでに存在しているものを意味します。

関手のコードを書くことは、移動平均クラスの内蔵インスタンスを使うことができるので、ここでは必ずしもする必要はありません。CiMAクラスとCiDEMAクラスは、移動平均と二重指数移動平均のニーズに簡単に対応できます。それにもかかわらず、私たちはこれらの平均値を扱うためにCObjectsクラスのインスタンスを宣言し、使用しています。

...

   CObjects<double>        m_o_average_a;
   CObjects<double>        m_o_average_b;
   CObjects<double>        m_o_average_c;
   CObjects<double>        m_o_average_d;
...

前回の記事のように、自然変換バッファを初期化する必要があるでしょう。シグナルを読み取る能力は最初から重要だからです。前回の記事の場合と同様に、それらのサイズは入力パラメータm_transformationsであるため、この重要なステップは、前回の記事で実装したものとほぼ同じ方法で処理されます。主な違いは、バッファに使用する移動平均インスタンスが4つあることです。これらの値の更新は、初期化と少し似ていますが、以下のリストのように処理されます。

//+------------------------------------------------------------------+
//| Refresh function from Natural Transformations.                   |
//+------------------------------------------------------------------+
void CTrailingCT::Refresh(void)
   {
      if(!m_init)
      {
         Init();
      }
      else
      {
         m_close.Refresh(-1);
         
         int _x=StartIndex();
         
         for(int i=m_functor_ab+m_functor_ab-1;i>0;i--)
         {
            m_e_price.Let();m_e_price.Cardinality(1);m_o_prices.Get(i,m_e_price);m_o_prices.Set(i-1,m_e_price);
         }
         
         double _p=m_close.GetData(_x);
         m_e_price.Let();m_e_price.Cardinality(1);m_e_price.Set(0,_p);m_o_prices.Set(0,m_e_price);
         
         for(int i=0;i<m_transformations+1;i++)
         {
            double _a=0.0;
            for(int ii=i;ii<m_functor_ab+i;ii++)
            {
               _a+=m_close.GetData(_x+ii);
            }
            _a/=m_functor_ab;
            m_e_price.Let();m_e_price.Cardinality(1);m_e_price.Set(0,_a);m_o_average_a.Set(i,m_e_price);
            //
            double _b=0.0;
            for(int ii=i;ii<m_functor_cd+i;ii++)
            {
               m_e_price.Let();m_e_price.Cardinality(1);m_o_average_a.Get(i,m_e_price);
               double _b_i=0.0;m_e_price.Set(0,_b_i);
               _b+=_b_i;
            }
            _b/=m_functor_cd;
            m_e_price.Let();m_e_price.Cardinality(1);m_e_price.Set(0,_b);m_o_average_b.Set(i,m_e_price);
            //
            double _c=0.0;
            for(int ii=i;ii<m_functor_ab+i;ii++)
            {
               _c+=m_close.GetData(_x+ii);
            }
            _c/=m_functor_ab;
            m_e_price.Let();m_e_price.Cardinality(1);m_e_price.Set(0,_c);m_o_average_c.Set(i,m_e_price);
            //
            double _d=0.0;
            for(int ii=i;ii<m_functor_cd+i;ii++)
            {
               m_e_price.Let();m_e_price.Cardinality(1);m_o_average_c.Get(i,m_e_price);
               double _d_i=0.0;m_e_price.Set(0,_d_i);
               _d+=_d_i;
            }
            _d/=m_functor_cd;
            m_e_price.Let();m_e_price.Cardinality(1);m_e_price.Set(0,_d);m_o_average_d.Set(i,m_e_price);
         }
         
         
         for(int i=m_transformations-1;i>0;i--)
         {
            m_natural_transformations_ac[i]=m_natural_transformations_ac[i-1];
            m_natural_transformations_bd[i]=m_natural_transformations_bd[i-1];
         }
         //
         double _a=0.0;
         m_e_price.Let();m_e_price.Cardinality(1);m_o_average_a.Get(0,m_e_price);m_e_price.Get(0,_a);
         double _b=0.0;
         m_e_price.Let();m_e_price.Cardinality(1);m_o_average_b.Get(0,m_e_price);m_e_price.Get(0,_b);
         double _c=0.0;
         m_e_price.Let();m_e_price.Cardinality(1);m_o_average_c.Get(0,m_e_price);m_e_price.Get(0,_c);
         double _d=0.0;
         m_e_price.Let();m_e_price.Cardinality(1);m_o_average_d.Get(0,m_e_price);m_e_price.Get(0,_d);
         
         m_natural_transformations_ac[0]=_a-_c;
         m_natural_transformations_bd[0]=_b-_d;
      }
   }


更新関数には少々問題があります。圏1の基本である終値ポインタの更新から始めます。この圏のソロオブジェクトに最新の値を割り当てる前に、このオブジェクト内のすべての既存要素をシフトする必要があります。次に、圏2の2つの対象でほぼ同じことをします。ここでの違いは、まず各期間の移動平均を計算する必要があり、これが終わったら、圏1でおこなったように、両方のオブジェクトの値をシフトした後に値を割り当てることです。この後、圏3のオブジェクトを扱わなければなりません。4つありますが、前述の通り、私たちの予想では2つしか使用しません。これらのオブジェクトの値は、先ほど入力した2つのオブジェクトから取得し、移動平均の2倍から移動平均の移動平均を引くDEMA式を少し変更して、単純に前者を計算します。移動平均の移動平均です。

MQL5ウィザードでEAトレーリングクラスのインスタンスが組み立てられると、テスト実行のためのEURUSD日足価格データとのモデルの統合がおこなわれます。その方法については、いつものようにここにガイドがあります。前述したように、このEAシグナルクラスのインスタンスとして、素晴らしいオシレーターでこのEAを組み立てます。

EURUSD履歴データのバックテストと分析は2020.01.01から2023.01.01までおこなわれています。最良の最適化結果からのテストレポートを以下に示します。

r1

レポートの重要な注目点は、通常、MAE利益相関、保有期間と利益相関です。この2つは通常、完全なレポートの一番下にグラフとして表示されます。上記で紹介したのは、誰もが注目するような共通の指標を取り上げた要約です。


テストとフォワードラン

2023.01.01から2023.08.01まで、バックテストのベストセッティングでフォワードを実行すると、以下のようなレポートが得られます。

r2

望むようなウォークが得られなかったということは、通常、次の2つのうちのいずれかを意味します。モデル(使用したトレーリングクラス)のテーゼはランダムであり、取引システムで信頼することはできない、あるいは、トレーリングクラスのコードに制御構造バグのようなバグがあり、そのアイデアを間違って実装したということです。しかし、肝に銘じておくべき重要なことは、仮に前向きな歩みを得たとしても、もっと長い期間にわたって、より包括的なテストをおこなう必要があるということです。外国為替の場合、これは最大20年を意味する可能性があり、これらのテストには実際のティックパスが含まれる必要があります。実際、これを強調するために、移動平均トレンドはポジションの方向にある必要があるため、EAによって生成される独特のトレーリングストップ調整「シグナル」はあまり頻繁には発生しません。このため、トレーリングクラスの有効性は短いテスト期間では十分にテストされておらず、この3年間はそのうちの1つであると主張することもできます。さらに、これらの結果は、トレーリングクラスが素晴らしいオシレーターとペアになっていることに基づいていますが、他のエントリシグナルとペアになっている場合、パフォーマンスはどうなるでしょうか。評価を下す際には、こうした懸念やその他の懸念もすべて考慮に入れる必要があります。

実データでのテストは、このEAや、ここで取り上げたことを組み込んだその変種を展開する前におこなうべきもう1つの必要なステップです。この最初のステップは、目的のブローカーでデモ口座を設定することです。理想的には、ライブで実行する口座を複製する口座タイプを使用します。その後、取引する銘柄のチャートにEAを貼り付けます。MetaQuoteのVPSサービスを利用するのは、セットアップの手間が省け、価格も手頃なので良いアイデアかもしれませんが、どのような選択をするにせよ、期待されるパフォーマンスと取引結果を確実にするために、EAのログを監視する必要性が最も重要です。MetaQuoteのVPSも一般的なVPSサーバーも、これを簡単に実現できます。また、リアルタイムのテスト口座は、テストが肯定的であった場合に展開されることを意図した資本額で開始されるべきです。最後に、特にここで紹介したアイデアが何らかの形でニュースに依存する別の取引システムの一部として追加される場合、個人の取引ジャーナルも良いアイデアになる可能性があります。MQL5ストラテジーテスターでは、経済指標カレンダーイベントを読み取りながらのテストがまだ許可されていないため、レビュー時に長期にわたる各ニュース項目の相対的な重要性を簡単に評価できるように、これらを自分で記録すると役立つ場合があります。

結果を解釈し、次に何をすべきかを決定するのは非常に主観的なことですが、1つの目安として、目標条件を満たすのに十分な利益を上げているシステムでは、利益とMAEの相関は正であるべきであり、各取引の利益と同様に保有期間も正の相関を持つべきであると主張することができます。最後の指標はしばしば期待値と呼ばれますが、残念ながらストラテジーテスターのレポートではこの値は計算されません。


結論

まとめると、自然変換の水平合成が予測にどのように役立つかを見てきました。この記事で検討した予測は、ボラティリティの指標ともいえる価格帯のものでした。これらの予測は、この連載で何度かおこなってきたように、ポジションのトレーリングストップを調整することで活用されました。サンプル訓練の結果は肯定的でしたが、サンプル外またはウォークフォワードの結果はそうではありませんでした。これは後退ではありますが、システムの有効性を判断する前に、より長いスパンでより包括的なテストをおこなう必要性を否定するものではありません。

これらの結果が取引システム開発に与える影響としては、異なるエントリシグナルクラスと組み合わせてより良い結果を得たり、あるいは異なるテストシナリオを提示するために入力データセットを変更したりすることが考えられます。

いつものように、読者の皆さんには、添付のコードを満足のいくまで修正し、テストするために、これらやその他のあらゆる手段を探求していただきたいと思います。


参照文献

参照はいつものように主にウィキペディアからのものですが、今回はInvestopedia.も使用されています。

EAで添付のコードを組み立て、使用するための追加リソースは、こちらこちらにあります。


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

添付されたファイル |
ct_22.mqh (29.34 KB)
TrailingCT_23.mqh (16.88 KB)
ONNXをマスターする:MQL5トレーダーにとってのゲームチェンジャー ONNXをマスターする:MQL5トレーダーにとってのゲームチェンジャー
機械学習モデルを交換するための強力なオープン標準形式であるONNXの世界に飛び込んでみましょう。ONNXを活用することでMQL5のアルゴリズム取引にどのような変革がもたらされ、トレーダーが最先端のAIモデルをシームレスに統合し、戦略を新たな高みに引き上げることができるようになるかがわかります。クロスプラットフォーム互換性の秘密を明らかにし、MQL5取引の取り組みでONNXの可能性を最大限に引き出す方法を学びましょう。ONNXをマスターするためのこの包括的なガイドで取引ゲームを向上させましょう。
MQL5における代替リスクリターン指標 MQL5における代替リスクリターン指標
本稿では、シャープレシオの代替指標とされるいくつかのリスクリターン指標の実装を紹介し、その特徴を分析するために仮想資本曲線を検証します。
MQL5で日付と時刻を扱う方法を学ぶ MQL5で日付と時刻を扱う方法を学ぶ
日付と時刻の取り扱いという、新しい重要なトピックについての新しい記事です。トレーダーとして、あるいは取引ツールのプログラマーとして、日付と時間という2つの側面をいかにうまく、効果的に扱うかを理解することは非常に重要です。そこで今回は、効果的な取引ツールを円滑かつシンプルに作成するために、日付と時刻をどのように扱えばよいのか、私ができる範囲で重要な情報をお伝えします。
MetaTrader 5用のMQTTクライアントの開発:TDDアプローチ(第3部) MetaTrader 5用のMQTTクライアントの開発:TDDアプローチ(第3部)
この記事は、MQTTプロトコルのネイティブMQL5クライアントの開発手順を説明する連載の第3部です。今回は、CONNECT/CONNACKパケット交換の操作時の動作部分を実装するために、テスト駆動開発をどのように使用しているかについて詳しく説明します。この手順の最後に、クライアントは、接続の試みから生じる可能性のあるサーバー結果のどれに対しても、絶対的に、適切に振る舞うことができなければなりません。