知っておくべきMQL5ウィザードのテクニック(第09回):K平均法とフラクタル波の組み合わせ
はじめに
この記事では、 K平均法を詳しく掘り下げて、MQL5ウィザードのおかげで実装およびテストできる、考えられる簡単なアイデアを引き続き見ていきます。これは、この前の記事で説明したAHC(英語)と同様、データを分類するための教師なし学習アプローチ です。
したがって、本題に入る直前に、AHCで説明した内容を要約し、それがK平均法とどのように対照されるかを確認することが役立つかもしれません。AHC(凝集型階層クラスタリング)アルゴリズムは、分類されるデータセット内の各データポイントをクラスタとして扱うことによって初期化されます。その後、アルゴリズムは、近接度に応じてそれらを反復的にクラスタにマージします。通常、クラスタの数は事前に決定されませんが、分析者は、すべてのデータポイントが1つのクラスタにマージされたときの最終出力である、構築された樹状図を確認することでこれを決定できます。あるいは、その記事で見たように、分析者がクラスタの数を念頭に置いている場合、出力樹状図はクラスタ数が分析者の最初の数値と一致するレベル/高さで終了します。実際、樹状図をどこで切断するかに応じて、異なるクラスタ番号が得られます。
一方、 K平均法は、分析者が事前に設定した数値に基づいてクラスタの中心(セントロイド)を無作為に選択することから始まります。次に、最も近い中心からの各データ ポイントの文さんが決定され、分散が各クラスターで最小になるまで中心/重心値に対して繰り返し調整がおこなわれます。
デフォルトでは、K平均法は実際には非常に低速で非効率的です。そのため、これがナイーブK平均法と呼ばれることが多く、「ナイーブ」という言葉は、より高速な実装があることを意味します。この面倒な作業の一部は、最適化の開始時にデータセットに初期重心を無作為に割り当てることから生じます。さらに、ランダムな重心が選択された後、ロイドのアルゴリズム(英語)が多くの場合、正しい重心、したがってカテゴリ値に到達するために使用されます。ロイドのアルゴリズムには、次のような補足と代替手段があります。JenksのNaturalBreaks(英語)は、選択した重心までの距離ではなくクラスタの平均に焦点を当てます。K-Median(英語)は、その名前が示すように、理想的な分類を導く際の代理として、重心や平均ではなくクラスタ中央値を使用します。k-medoids(英語)は、Wikipediaによると、各クラスタ内の実際のデータポイントを潜在的な重心として使用するため、ノイズや外れ値に対してより堅牢になります。最後に、ファジーモードクラスタリング(英語)では、クラスタの境界が明確ではなく、データポイントが複数のクラスタに属する可能性があり、実際にその傾向があります。この最後の形式では、各データポイントを「分類」するのではなく、特定のデータポイントが該当する各クラスタにどの程度属しているかを定量化する回帰重みが割り当てられるため、興味深いものです。
この記事の目的は、より効率的であると宣伝されているもう1つのタイプのK平均法実装を紹介することです。K-Means++法です。このアルゴリズムは、デフォルトのナイーブK平均法などのロイド法に依存していますが、ランダムな重心の選択に対する最初のアプローチが異なります。このアプローチは、ナイーブK平均法ほど「ランダム」ではなく、このため、後者よりもはるかに高速かつ効率的に収束する傾向があります。
アルゴリズムの比較
K平均法とK-Median
K平均法はクラスタ点とその重心の間の二乗ユークリッド距離を最小化しますが、K-Medianは特定のクラスタ内の中央値からの点の絶対距離の合計を最小化します(L1-Norm(英語))。この区別により、K-Medianが外れ値の影響を受けにくくなり、クラスタの中心がすべての点の平均ではなく中央値になるため、クラスタがすべてのデータ点をよりよく表すことができると主張されています。K-MedianはL1ノルムに基づくアルゴリズムに依存するのに対し、k平均法はK-Means++とロイドアルゴリズムを使用するため、計算アプローチも異なります。したがって、使用例では、K平均法は球状または均等に広がったデータセットを処理できる一方、K-Medianは不規則で奇妙な形状のデータセットに適していると見なされます。最後に、K-Medianは平均よりもクラスタをよく表す傾向があるため、解釈に関してはK-Medianが好まれる傾向があります。
K平均法vs Jenks-Natural-Breaks
K平均法同様、Jenks-Natural-Breaksアルゴリズムは、データポイントから重心までの距離を可能な限り最小化しようとしますが、微妙な違いは、このアルゴリズムがこれらのクラスを区別できるように、これらのクラスを可能な限り遠くに描画しようとするという事実にあります。これは、データの「自然な集団化」を識別することによって実現されます。これらの「自然な集団化」は、クラスタ内で分散が大幅に増加する点で識別され、これらの点はブレークと呼ばれ、アルゴリズムの名前の由来となっています。各クラスタ内の分散を最小限に抑えることで、切れ目が強調されます。これは、回帰型または連続型よりも分類スタイルのデータセットに適しています。これらすべてにより、K-Medianアルゴリズムと同様に、典型的なk平均法と比較した場合、外れ値に対する感度と全体的な解釈において利点が得られます。
K平均法とK-Medoids
前述したように、K-medoidsは最初は概念的な重心点ではなく実際のデータに依存します。この点では、凝集型階層分類によく似ていますが、ここでは樹状図は作成されません。重心として使用される選択されたデータポイントは、クラスタ内の他のすべてのデータポイントまでの距離が最も小さいデータポイントです。この選択では、マンハッタン距離またはコサイン類似度(英語)のようなさまざまな距離測定技術も使用できます。セントロイドは実際のデータポイントであるため、JunksやK-Medianと同様に、K平均法よりも基になるデータをよりよく表していると主張できますが、特に大規模なデータセットを処理する場合は、計算効率がより低くなります。
K平均法とファジークラスタリング
前述のファジークラスタリングは、各データポイントに回帰重みを提供します。この重みは、使用されているクラスタの数に応じてベクトル形式になります。この重みは、最終的な重心を使用するK平均法とは異なり、ファジープロトタイプ(メンバーシップ関数)を使用することにより、クラスタごとに0.0~1.0の範囲になります。これにより、より多くの情報が提供される傾向があるため、データをより適切に表すことができます。上記のすべての点で典型的なK平均法を上回るスコアを示しますが、主な欠点は、予想どおりに負荷がかかる計算にあることです。
K-Means++
ナイーブなk平均法をより効率的にするために、通常、そしてこの記事では、初期重心がランダムではなく、より比例してデータ全体に分散するK-Means++初期化が使用されます。これはテストにより、はるかに高速なソリューションとターゲット重心への収束につながりました。全体的にクラスタの品質が向上し、外れ値のデータポイントだけでなく、重心ポイントの最初の選択に対する感度も低くなります。
データ
AHCに関する記事で実装したように、AlgLibのK平均法対応クラスを使用して、その記事で用意したものと単純で同様のアルゴリズムを開発し、相互検証された結果が得られるかどうかを確認します。テスト対象の証券はGBPUSDで、2022.01.01から2023.02.01までテストを実行し、その後、その日付から2023.10.01までウォークフォワードを実行します。日次の時間枠を使用し、テスト期間中に実際のティックで最終実行をおこないます。
構造体
クラスタを編成するために使用されるデータ構造体は、AHCの記事で説明したものと同一であり、実際、使用される手順とシグナルのアイデアはほぼ同じです。主な違いは、凝集型クラスタリングを使用した場合、ターゲットクラスタ番号と一致するレベルでクラスタを取得する関数を実行する必要があったため、関数'ClusterizerGetKClusters'を呼び出しましたが、ここでは実行しないということです。これに加えて、構造体が実際に価格情報を確実に受け取るように細心の注意を払う必要があり、そのために、以下の短いスニペットに見られるように、無効な数値がないか頻繁に確認します。
double _dbl_min=-1000.0,_dbl_max=1000.0; for(int i=0;i<m_training_points;i++) { for(int ii=0;ii<m_point_features;ii++) { double _value=m_close.GetData(StartIndex()+i)-m_close.GetData(StartIndex()+ii+i+1); if(_dbl_min>=_value||!MathIsValidNumber(_value)||_value>=_dbl_max){ _value=0.0; } m_data.x.Set(i,ii,_value); matrix _m=m_data.x.ToMatrix();if(_m.HasNan()){ _m.ReplaceNan(0.0); }m_data.x=CMatrixDouble(_m); } if(i>0)//assign classifier only for data points for which eventual bar range is known { double _value=m_close.GetData(StartIndex()+i-1)-m_close.GetData(StartIndex()+i); if(_dbl_min>=_value||!MathIsValidNumber(_value)||_value>=_dbl_max){ _value=0.0; } m_data.y.Set(i-1,_value); vector _v=m_data.y.ToVector();if(_v.HasNan()){ _v.ReplaceNan(0.0); }m_data.y=CRowDouble(_v); } }
ALGLIB
AlgLibライブラリはこれらの連載ですでに何度も参照されているため、クラスタを形成するコードにすぐに進みます。ライブラリ内の2つの関数に焦点を当てます。SelectInitialCentersは、プロセス全体を迅速化する上で重要です。これは、前述したように、クラスタの初期選択がランダムすぎると、適切なクラスタに収束するまでの時間が長くなる傾向があるためです。この関数が実行されると、ロイドアルゴリズムを使用して初期クラスタ選択を微調整し、そのために関数KMeansGenerateInternalを使用します。
使用可能な関数を持つ初期クラスタの選択は、無作為におこなうか、K-Means++を使用するか、高速貪欲初期化を使用しておこなうかの3つの方法のいずれかでおこなうことができます。それぞれについて簡単に説明しましょう。他の2つのケースと同様、ランダムなクラスタ選択では、出力クラスタはctという名前の出力行列に格納されます。これにより、ctの行数が目的のクラスタ番号と一致するように各行がクラスタを表し、列はデータセット内の各データポイントの特徴またはベクトル基数に等しくなります。したがって、無作為オプションは、入力データセットから無作為に選択されたデータポイントをctの各行に1回だけ割り当てます。これを以下に示します。
//--- Random initialization if(initalgo==1) { for(i=0; i<k; i++) { j=CHighQualityRand::HQRndUniformI(rs,npoints); ct.Row(i,xy[j]+0); } return; }
K-Means++では、無作為な中心を選択することから始めますが、前にすべてのクラスタに対してこれをおこなったのとは異なり、最初のクラスタに対してのみです。次に、各データセットポイントと無作為に選択されたクラスタ中心の間の距離を測定し、これらの距離の二乗和を行(または潜在的なクラスタ)ごとに記録します。この合計がゼロの場合は、そのクラスタの無作為な重心を選択するだけです。変数sに格納されているすべての非ゼロ和について、無作為に選択した初期クラスタから最も遠い点を選択します。コードはかなり複雑ですが、これは短いスニペットであり、コメントでさらに詳しく説明されています。
//--- k-means++ initialization if(initalgo==2) { //--- Prepare distances array. //--- Select initial center at random. initbuf.m_ra0=vector<double>::Full(npoints,CMath::m_maxrealnumber); ptidx=CHighQualityRand::HQRndUniformI(rs,npoints); ct.Row(0,xy[ptidx]+0); //--- For each newly added center repeat: //--- * reevaluate distances from points to best centers //--- * sample points with probability dependent on distance //--- * add new center for(cidx=0; cidx<k-1; cidx++) { //--- Reevaluate distances s=0.0; for(i=0; i<npoints; i++) { v=0.0; for(j=0; j<=nvars-1; j++) { vv=xy.Get(i,j)-ct.Get(cidx,j); v+=vv*vv; } if(v<initbuf.m_ra0[i]) initbuf.m_ra0.Set(i,v); s+=initbuf.m_ra0[i]; } // //--- If all distances are zero, it means that we can not find enough //--- distinct points. In this case we just select non-distinct center //--- at random and continue iterations. This issue will be handled //--- later in the FixCenters() function. // if(s==0.0) { ptidx=CHighQualityRand::HQRndUniformI(rs,npoints); ct.Row(cidx+1,xy[ptidx]+0); continue; } //--- Select point as center using its distance. //--- We also handle situation when because of rounding errors //--- no point was selected - in this case, last non-zero one //--- will be used. v=CHighQualityRand::HQRndUniformR(rs); vv=0.0; lastnz=-1; ptidx=-1; for(i=0; i<npoints; i++) { if(initbuf.m_ra0[i]==0.0) continue; lastnz=i; vv+=initbuf.m_ra0[i]; if(v<=vv/s) { ptidx=i; break; } } if(!CAp::Assert(lastnz>=0,__FUNCTION__": integrity error")) return; if(ptidx<0) ptidx=lastnz; ct.Row(cidx+1,xy[ptidx]+0); } return; }
いつものように、AlgLibはいくつかの公開ドキュメントを共有しているため、さらに明確にするための参照となります。
最後に、K-Means++と呼ばれるK平均法のバリエーションからインスピレーションを得た、高速貪欲な初期化アルゴリズムについてですが、いくつかのラウンドが実行され、各ラウンドごとに、現在選択されている重心に最も近い距離の計算がおこなわれます。次に、予想されるクラスタサイズの約半分の独立したサンプリングが実行されます。この場合、ポイントを選択する確率は現在の重心からの距離に比例し、サンプルされたポイントの数がクラスタを埋める数の2倍になるまでこれが繰り返されます。次に、選択された特大サンプルを使用して、重心から最も遠い点に優先順位が与えられて、より小さいサンプルサイズが達成されるまで、このサンプルから「貪欲選択」が実行されます。非常に計算量が多く複雑なプロセスのコードをコメント付きで以下に示します。
//--- "Fast-greedy" algorithm based on "Scalable k-means++". //--- We perform several rounds, within each round we sample about 0.5*K points //--- (not exactly 0.5*K) until we have 2*K points sampled. Before each round //--- we calculate distances from dataset points to closest points sampled so far. //--- We sample dataset points independently using distance xtimes 0.5*K divided by total //--- as probability (similar to k-means++, but each point is sampled independently; //--- after each round we have roughtly 0.5*K points added to sample). //--- After sampling is done, we run "greedy" version of k-means++ on this subsample //--- which selects most distant point on every round. if(initalgo==3) { //--- Prepare arrays. //--- Select initial center at random, add it to "new" part of sample, //--- which is stored at the beginning of the array samplesize=2*k; samplescale=0.5*k; CApServ::RMatrixSetLengthAtLeast(initbuf.m_rm0,samplesize,nvars); ptidx=CHighQualityRand::HQRndUniformI(rs,npoints); initbuf.m_rm0.Row(0,xy[ptidx]+0); samplescntnew=1; samplescntall=1; initbuf.m_ra1=vector<double>::Zeros(npoints); CApServ::IVectorSetLengthAtLeast(initbuf.m_ia1,npoints); initbuf.m_ra0=vector<double>::Full(npoints,CMath::m_maxrealnumber); //--- Repeat until samples count is 2*K while(samplescntall<samplesize) { //--- Evaluate distances from points to NEW centers, store to RA1. //--- Reset counter of "new" centers. KMeansUpdateDistances(xy,0,npoints,nvars,initbuf.m_rm0,samplescntall-samplescntnew,samplescntall,initbuf.m_ia1,initbuf.m_ra1); samplescntnew=0; //--- Merge new distances with old ones. //--- Calculate sum of distances, if sum is exactly zero - fill sample //--- by randomly selected points and terminate. s=0.0; for(i=0; i<npoints; i++) { initbuf.m_ra0.Set(i,MathMin(initbuf.m_ra0[i],initbuf.m_ra1[i])); s+=initbuf.m_ra0[i]; } if(s==0.0) { while(samplescntall<samplesize) { ptidx=CHighQualityRand::HQRndUniformI(rs,npoints); initbuf.m_rm0.Row(samplescntall,xy[ptidx]+0); samplescntall++; samplescntnew++; } break; } //--- Sample points independently. for(i=0; i<npoints; i++) { if(samplescntall==samplesize) break; if(initbuf.m_ra0[i]==0.0) continue; if(CHighQualityRand::HQRndUniformR(rs)<=(samplescale*initbuf.m_ra0[i]/s)) { initbuf.m_rm0.Row(samplescntall,xy[i]+0); samplescntall++; samplescntnew++; } } } //--- Run greedy version of k-means on sampled points initbuf.m_ra0=vector<double>::Full(samplescntall,CMath::m_maxrealnumber); ptidx=CHighQualityRand::HQRndUniformI(rs,samplescntall); ct.Row(0,initbuf.m_rm0[ptidx]+0); for(cidx=0; cidx<k-1; cidx++) { //--- Reevaluate distances for(i=0; i<samplescntall; i++) { v=0.0; for(j=0; j<nvars; j++) { vv=initbuf.m_rm0.Get(i,j)-ct.Get(cidx,j); v+=vv*vv; } if(v<initbuf.m_ra0[i]) initbuf.m_ra0.Set(i,v); } //--- Select point as center in greedy manner - most distant //--- point is selected. ptidx=0; for(i=0; i<samplescntall; i++) { if(initbuf.m_ra0[i]>initbuf.m_ra0[ptidx]) ptidx=i; } ct.Row(cidx+1,initbuf.m_rm0[ptidx]+0); } return; }
このプロセスにより、代表的な重心と次のフェーズの効率が確保されます。
初期重心を選択すると、KMeansGenerateInternalのコア機能であるロイドアルゴリズムが開始されます。AlgLibによる実装は複雑に見えますが、ロイドアルゴリズムの基本は、各クラスタの重心を反復的に検索し、各クラスター内でその重心から構成点までの距離が最小になるように、データポイントを1つのクラスタから別のクラスタに移動することによって各クラスタを再定義することです。
樹状図に関する記事と同様に、この記事でも、データセットポイントは単純に証券の終値の変化であり、テストではGBPUSDでした。
予測
AHCのようなK平均法は本質的に教師なしの分類であるため、以前と同様、回帰または予測を実行したい場合は、クラスタ化されたデータセットによって遅延された「y」列データを追加する必要があります。したがって、この「y」データも終値の変更になりますが、クラスタに効果的にラベルを付けるために、クラスタ化されたデータより1バー先になります。また、効率を高めるために、クラスタ化されるx行列データ セットを満たす同じforループによって「y」データセットが設定されます。これは、以下の短いコードに示されています。
if(i>0)//assign classifier only for data points for which eventual bar range is known { double _value=m_close.GetData(StartIndex()+i-1)-m_close.GetData(StartIndex()+i); if(_dbl_min>=_value||!MathIsValidNumber(_value)||_value>=_dbl_max){ _value=0.0; } m_data.y.Set(i-1,_value); vector _v=m_data.y.ToVector();if(_v.HasNan()){ _v.ReplaceNan(0.0); }m_data.y=CRowDouble(_v); }
x行列とy配列にデータが入力されると、クラスタの定義が上記の手順で進み、その後、現在の終値変化のクラスタ、つまりx行列の最上位行が特定されます。他のデータポイントとともにクラスタ化処理されるため、クラスタインデックスが作成されます。このクラスタインデックスを使用して、それをすでに「ラベル付けされた」データポイント、つまり最終的な終値の変化がわかっているデータと比較し、これらの最終的な変化の合計を取得します。この合計を使用すると、現在の範囲(またはボラティリティ)で正規化すると、0~1の範囲の重み付けが得られる平均変化を簡単に取得できます。
//+------------------------------------------------------------------+ //| "Voting" that price will fall. | //+------------------------------------------------------------------+ int CSignalKMEANS::ShortCondition(void) { ... double _output=GetOutput(); int _range_size=1; double _range=m_high.GetData(m_high.MaxIndex(StartIndex(),StartIndex()+_range_size))-m_low.GetData(m_low.MinIndex(StartIndex(),StartIndex()+_range_size)); _output/=fmax(_range,m_symbol.Point()); _output*=100.0; if(_output<0.0){ result=int(fmax(-100.0,round(_output)))*-1; } ... }
LongCondition関数とShortCondition関数は0~100の範囲の値を返すため、正規化された値に100を掛ける必要があります。
評価と結果
2022.01.01から2023.02.01までの期間のバックテストでは、次のレポートが得られます。
このレポートは、最適化の実行から取得した次の入力に依存しています。
これらの設定を2023.02.02から2023.10.01まで進めると、次のレポートが得られます。
この非常に短いテスト期間では少し有望ですが、いつものように、より熱心に、より長期間にわたってテストすることをお勧めします。
フラクタル波による実装
ここで、終値の変化ではなく、フラクタル指標からのデータを使用するオプションを考えてみましょう。フラクタル指標は、更新時のバッファに各インデックスの指標値や価格が含まれていないため、特にエキスパートアドバイザー(EA)を使用して実装しようとする場合、そのまま使用するのは少し困難です。各バッファインデックスを確認して、実際に「フラクタル」(つまり、価格)があるかどうかを確認する必要があります。フラクタルがない場合、デフォルトのプレースホルダーは最大double値になります。改訂されたGetOutput関数内では、次のようにフラクタルデータを準備します。
//+------------------------------------------------------------------+ //| Get k-means cluster output from identified cluster. | //+------------------------------------------------------------------+ double CSignalKMEANS::GetOutput() { ... int _size=m_training_points+m_point_features+1,_index=0; for(int i=0;i<m_fractals.Available();i++) { double _0=m_fractals.GetData(0,i); double _1=m_fractals.GetData(1,i); if(_0!=DBL_MAX||_1!=DBL_MAX) { double _v=0.0; if(_0!=DBL_MAX){_v=_0;} if(_1!=DBL_MAX){_v=_1;} if(!m_loaded){ m_wave[_index]=_v; _index++; } else { for(int i=_size-1;i>0;i--){ m_wave[i]=m_wave[i-1]; } m_wave[0]=_v; break; } } if(_index>=int(m_wave.Size())){ break; } } if(!m_loaded){ m_loaded=true; } if(m_wave[_size-1]==0.0){ return(0.0); } ... ... }
実際の価格フラクタルを取得するには、まずフラクタル指標オブジェクトを適切に更新する必要があります。これが完了したら、利用可能なバッファインデックスの全体数を取得する必要があります。この値は、フラクタル価格ポイントを探すときにforループでループする必要があるインデックスの数を示します。その際、フラクタル指標にはインデックス0と1の2つのバッファがあることに注意する必要があります。0のバッファインデックスは高フラクタル用であり、1のインデックスバッファは低フラクタル用です。これは、forループ内でこれらのインデックスバッファのフラクタル価格ポイントを同時に確認する2つの値を持ち、それらのいずれかが価格を記録すると(一度に1つだけ価格を登録できる)、この値をベクトルm_waveに追加することを意味します。
通常、フラクタル価格ポイントの検索制限として機能する、利用可能なフラクタルインデックスの数は制限されています。つまり、12個のインデックスのウェーブバッファがあると言いたい場合でも、最初の実行または最初の価格バーで最終的に3個しか取得できない可能性があります。これは、ウェーブバッファが、取得できる価格インデックスを保存し、新しいフラクタル価格が利用可能になってバッファに追加できるようになるのを待つ適切なバッファのように機能する必要があることを意味します。このプロセスは、バッファがいっぱいになるまで続行されます。その間、バッファはまだファイルされておらず、初期化されていないため、EAはシグナルを処理できず、本質的には初期化フェーズになります。
したがって、フラクタルを取得する際に使用されるバッファのサイズが重要になります。これらのフラクタルは、フラクタル価格変更を使用するシステムでK平均法アルゴリズムに入力されるため、このバッファのサイズが訓練ポイントの数、特徴の数、および1の合計です。最後に1を追加します。入力データ行列には訓練ポイントと特徴だけが必要ですが、追加の行はまだ回帰されていない、つまり'y'値を持たないポイントの現在の行であるためです。
残念ながらこの努力は必要ですが、これを乗り越えると、波のようなパターンでソートされた価格情報が提供されます。そして、ここでの主張は、各波の頂点間の変化、つまりフラクタル価格ポイントが、最初の実装で使用した終値の変化を置き換えることができるということです。
しかし皮肉なことに、この新しいEAをテストする際に、終値変更の場合のようにポジション価格エグジット(TPおよびSL)を使用しないという自由は認められず、代わりにTPを使用してテストする必要がありました。そして、テスト後は、バックテストが有望だったにもかかわらず、終値の変動でおこなったような最良の最適化結果を伴う収益性の高いフォワードテストを得ることができませんでした。以下にレポートを掲載します。
これらの取引の継続的で中断のない株価グラフを見ると、最初の実行は有望であるにもかかわらず、前進が期待できないことが明らかにわかります。
これは基本的に、このアイデアには見直しが必要であることを意味しており、その出発点の1つは、フラクタル指標を再検討し、おそらくフラクタル価格ポイントのみを備えているという点でより効率的なカスタムバージョンを用意すること、そして第2に、次に、各フラクタル ポイント間の最小価格変動をガイドまたは定量化するいくつかの入力を使用してカスタマイズできることです。
結論
まとめると、 K平均法と、生の終値とフラクタル価格データを使用した2つの異なる設定で、AlgLibによるすぐに使える実装をどのように実現できるかについて見てきました。
両方の設定の相互検証テストでは、予備段階では異なる結果が得られ、生の終値システムの方がフラクタル価格アプローチよりも有望であると考えられます。その理由と、これで使用されているソースコードを以下に共有します。
参照文献
付録
この記事に添付されているMQL5ウィザードに関するソースへの参照が役立つ場合があります。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/13915
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索