
Accumulation/Distributionへのインサイトと、そのゴール地点
はじめに
Accumulation/Distribution A/Dインジケーターは、一つ興味深い特徴があります - このインジケータチャートのトレンド線のブレークアウトは、特定の確率にてやがて来る価格チャートのトレンド線のブレークアウトを示唆します。
そのため、この事実をチェックしようと考え、A/Dチャートにすべてのトレンド線を描画してすぐに、この問題を手動で取り組むアプローチは、不便であると気付きました。そのため、A/Dチャートに自動的にトレンド線を描画する関数を開発し、価格チャートにシグナルインジケーターを設定しました。あなたのトレーディングシステムにて使用できるようにMQL4を用いてどのように実装できるかについてのステップごとの手順を紹介したいと思います。
この記事は、MQL4プログラミングに慣れ親しんでいない人にとってはとても役に立ち、面白い記事になるかと思います。この観点から最も簡単なコードの構造を用い、コーディングの方法を理解する上でわかりやすい情報を提示することに努めました。
1. タスクの設定
まず設定される必要のあるタスクを定義しましょう。
その関数は、 A/D チャートにて描画されるトレンド線間の交差地点を見つけ、そのインジケーターの線はクロスオーバーの方向 - 下方か上方かを示す値を返し、図で示すために価格チャートにシグナルインジケータを設定します。
それではこのタスクをより具体的な項目に分解してみましょう:
- その関すは、どのシンボル、どのタイムフレームでも作動する必要があります:
- この関数はEAの一部であるよう設計されているため、A/Dインジケーターはメインチャートにある必要はありません。
- シグナルインジケーターは、必ずしも価格チャートに表示される必要もありません - すべての計算はその関数で実行され、関数の処理を監視されるためにのみ表示されます。
- そのクロスオーバーは、異なる方向で発生します;上方の下方crossoverその関数はすべてのクロスオーバーを認識します。
その関数に関する限りこれは事実です。それではそのタスクを実現する方法を考察してみましょう。
2. A/Dインジケーターデータを配列に格納する
呼びだされた際に、その関数はいくつかの値を受け取ります:A/Dインジケーターデータを保持する配列、A/Dチャート極値を認識するための履歴バーの数、銘柄名とタイムフレームなどです。
トレンド線は、A/Dチャートの極値に基づいて描画され、上昇トレンド線が最低極値につながり、下降トレンド線が最高極値につながります。
上昇トレンド線は、二つの低い極値を必要とし、その最低値のある極値は現在のバーに近接して位置するその極値の左にあります。下降トレンドに関しても同様です:最高値を持つ極値は現在のバーの次の極値の左にあります。
もしこれらの必要条件を満たす極値が見つからなければ、トレンド線はこのティックに描画されません。これらのトレンド線を"global"と呼びます。
さらに、描画された"global"トレンド線に二つ線引きます。それは、右の二つの極値を必要とし、
- 上昇トレンドはより小さい値の極値を必要とします;
- 下降トレンドはより大きい値を持つ上方の極値を必要とします。
もしこれらの必要条件を満たす極値が見つからなければ、トレンド線はこのティックに描画されません。
これらのトレンド線を、 "local"とします。
結果として、以下をご覧になれます:
多くの場合、"global"極値を特定し、それらをつなげることによる下降トレンド線の描画の特異性により、"local"極値は必要ではありません。"local"極値を検索し、描画するオプションは、停止できます。
その関数の処理は、EAのグローバル変数が使用できる必要があり、特にA/D データを保持する配列を必要とします。EAのグローバル変数にそれを記述します:
double TempIND[]; // Array for storing A/D indicator data
それでは、そのインジケーターデータは、配列を格納するために読み込まれる必要があります;
int SignalCrossIND(double &TempIND[], int nBars, string sy, int tf) { if (sy=="" || sy=="0") sy = Symbol(); ArrayResize(TempIND,nBars); // Resize the array according to the size passed to the function for (int j=0; j<=nBars-1; j++) { TempIND[j]=iAD(sy,tf,j); // Write indicator data in the loop into the array } }
こちらに記述したものを分析してみましょう:
int SignalCrossIND(double &TempIND[], int nBars, stringsy, int tf)
その渡されるパラメータはTempIND[] - インジケーター値を保持する配列名、 nBars - その値が取得される履歴のバーの数 sy - 銘柄名、タイムフレーム tfなどであるその関数の定義
一般的にその関数は以下のように呼ばれます:
SignalCrossIND(TempIND, 30, NULL, 5);
TempIND はその配列名で、30 はバーの数で、NULL は、そのチャートの現在のシンボルで、5 は、M5タイムフレームです。
その次の行は:
if (sy=="" || sy=="0") sy = Symbol();
それは、その関数に渡されたトレーディング銘柄の名前をチェックし、もしその値がNULL か 0であれば、その現在のチャートシンボルが使用されます。
そして、nBars 変数のその関数に渡された値に応じてその配列を再調整し、ループ内の0バーからnBars-1バーまでのインジケーター値をそれに格納します。その配列のセルは、0から数えられるため、最初の数字は1の代わりに0で、最後の数字は結果として30の代わりに29です。したがって、nBars-1 を使用します(この例では、 30-1=29).
この段階で、その関数はA/Dインジケーターデータをその配列に格納します。A/Dチャートデータ配列の中で極値を特定する必要です。
3. A/Dインジケーターデータ配列の極値の特定
はじめましょう。その関数の処理は、グローバル変数のみではなく、その関数自身によって使用される変数を必要とします。
この段階で、上方と下方の極値(山と谷)を記述するため使用される二つの配列、その関数のために使用されるいくつかの変数と極値の時間を節約する二つの配列を宣言する必要があります。
// ------------------------------------------------------------------------------------------ // -------------------------------- Function variables -------------------------------------- // ------------------------------------------------------------------------------------------ double PeakUP[], PeakDN[]; // Declare peak/trough arrays datetime TimeDN[], TimeUP[]; // Arrays for storing the time of extrema int i, k; // Internal variables of the function
そして、やっとそれらの特定に移ります。
//------------------------------------------------------------------ // Filling arrays with data on peaks and troughs //------------------------------------------------------------------ k=0; // Initialize the trough array index for (i=2; i<=nBars-1; i++) // Run through the array of values { if (TempIND[i]<TempIND[i-1] && TempIND[i+1]>=TempIND[i]) // Trough identified { ArrayResize(PeakDN, k+1); // Resize the trough array according to the number of troughs identified ArrayResize(TimeDN, k+1); // Resize the trough time array according to the number of troughs PeakDN[k]=TempIND[i]; // Write the trough value into the trough array... TimeDN[k]=iTime(sy,tf,i); // ...and the time array k++; // Increase the trough array index } } // ----------------------------------------------------------------------------------------------------------- k=0; // Initialize the peak array index for (i=2; i<=nBars-1; i++) // Run through the array of values { if (TempIND[i]>TempIND[i-1] && TempIND[i+1]<=TempIND[i]) // Peak identified { ArrayResize(PeakUP, k+1); // Resize the peak array according to the number of peaks identified ArrayResize(TimeUP, k+1); // Resize the peak time array according to the number of peaks PeakUP[k]=TempIND[i]; // Write its value into the peak array... TimeUP[k]=iTime(sy,tf,i); // ...and the time array k++; // Increase the peak array index } }
上記では、なにがなされるか、どのようにそれがなされるかを明確にすることが必要です。まず、二つの個別のループをアレンジします - それぞれ上方と下方の極値の特定のためです。
下方の極値の特定のための最初のループの処理へ進みましょう:
変数kを0に初期化することから始めます - 特定された下方の極値が記述される配列の最初の要素を指すようにします。以前格納されたA/Dインジケーター値の配列で、0インデックス値ではなく、バー2に一致する3番目の値でループを回します(i=2;).
それはどうしてでしょうか?極値は、そのインジケータチャートのブレークポイントであるので、その最初のバーの値が2番目のバーのもの以上であり、2番目のバーの値が3番目のものよりも小さいところで、その配列の3番目の要素にそのインデックスをセットします(その要素群が0から番号付けされることに注意してください)0バーは、嘘のポジティブ値を排除するために計算においては使用されません。
そして、最初のの反復を始め、チェックします。
- バー2TempIND[i]の値がバー1TempIND[i-1]の値よりも小さく、
- バー3TempIND[i+1]の値がバー2TempIND[i]の値よりも大きいか否かチェックします。
もしこの条件が正であれば、谷の配列と谷の時間配列のサイズを1増やします。
ArrayResize(PeakDN, k+1); // Resize the trough array according to the number of troughs identified ArrayResize(TimeDN, k+1); // Resize the trough time array according to the number of troughs
そして、インジケーター値の配列のデータをこれらの配列に追加し、谷の配列インデックスを1増やします。
PeakDN[k]=TempIND[i]; // Write the trough value into the trough array... TimeDN[k]=iTime(sy,tf,i); // ...and the time array k++; // Increase the trough array index
その谷と谷の時間配列PeakDN 、TimeDNは、それぞれ特定された極値にて必要なデータを含み、その谷配列の次のセルのインデックスポイントも含みます。次のループの反復は、次の極値を特定する際、同じパターンをたどり、そのデータは、谷配列インデックス(k)が指す配列の次のセルに記入されます。
1番目・2番目・3番目のバーが異なった方法で比較されるということを除き、頂点(上部の極値)は、かなり類似するマナーにて特定されます:より小さいというサインとより大きいサインは交互にやり取りされます。この段階で、関数はA/Dインジケーターデータを読み取り、データを用いて配列に格納し、その中で極値を特定し、また極値の値と一を4つの指定された配列に保存します。
特定された極値に基づき描画するインジケーターチャートにてトレンド線を表示し始めることができます。
まず、"グローバル"トレンド線をインジケーターチャートにて表示しましょう。チャートの極値をつなげ、それらを描画するために、配列全体における最小の値の極値、主に参照する極値の二つの極値の特定から始めましょう。主な極値は、谷の配列の最初に近い位置に配置され、2に等しいように厳格に設定されます。従って、インジケーターチャートでの現在のバーに最も近い二つの極値は、考慮されません(発生するわずかな変化に反応する必要がなく、価格の動きにある程度の自由を残すためです)
その主な極値が特定されたので、その極値の左に位置する配列の最小の値の極値を見つける必要があります。この極値はインジケーターチャートの最下部に位置し、2番目の極値の最後の特定のための開始地点として機能します。
最下部の極値と主な極値をつなげるという方法でのトレンドラインの描画はトレンド線が特定された最下部の極値よりも高い極値によって交差されるという状況に繋がります。そして、そのトレンド線の初めはこの状況にてトレンド線を横切るその極値の場所に移動します。以下の数字は、そのようなケースを示す例です:
最下部の極値にて開始するその線は、メインの極値の方に伸びる中でより高い極値によって交差されます。従って、それは極値の配列を分析し、トレンド線の開始ポイントとして定義される際にトレンド線が後続の極値によって交差されるという状況に至らないものを特定するために必要です。
最小の極値に続く極値は、適していません - トレンド線がその地点から開始される場合、より高い極値によって交差されます:
さらなる試みを行ない、特定された右の極値に直接進みました。
それではすべてをコードとして記述していきましょう。上昇トレンド線を描くためにより小さい極値を、下降トレンド線を描くために上方の極値を用います。
最小の極値を通る上昇トレンドを描画する例を見てみましょう。
4. トレンド線描画のためのA/Dインジケーター極値の配列にて二つの極値の特定
二つの必要な極値の特定は、下部と上部の最小値とメイン極値の値と、それぞれの時間を渡し保存するための関数の内部変数とその配列をインデックス化する変数が使える必要がありあす。
私たちの関数の変数にそれらを追加します(この問題にまた立ち返らないために必要な関数の変数をすぐ使い始められるようにすぐに追加しておきます。)
// ------------------------------------------------------------------------------------------ // -------------------------------- Function variables ---------------------------- // ------------------------------------------------------------------------------------------ double PeakUP[], PeakDN[], // Declare peak/trough arrays yUP, yDN, yGUP, yGDN, // Coordinates of the crossing point between the trend line and indicator UP and DN on the 1st bar yUP2, yDN2, yGUP2, yGDN2, // Coordinates of the crossing point between the trend line and indicator UP and DN on the 2nd bar CheckCross, PreLastVarDN,LastVarDN, // Values of the last and last but one lower extrema PreLastVarUP,LastVarUP, // Values of the last and last but one upper extrema PivotPeakDN,PivotPeakUP, LowestPeakDN,HighestPeakDN,// Values of the lower minimum and maximum LowestPeakUP,HighestPeakUP,// Values of the upper minimum and maximum datetime TimeDN[], TimeUP[], // Arrays for storing bars of extrema PreLastTimeDN, LastTimeDN, // Time of the last and last but one lower extrema PreLastTimeUP, LastTimeUP, // Time of the last and last but one upper extrema PivotBarDN, PivotTimeDN, // Bar and time of the lower main extremum PivotBarUP, PivotTimeUP, // Bar and time of the upper main extremum LowestBarDN, LowestTimeDN, // Bar and time of the lower minimum HighestBarUP, HighestTimeUP;// Bar and time of the upper maximum int i, kup, kdn, pbar=2, m, l, t, asize, Index, // "Internal" variables WinID=WindowFind("A/D"); // AD window number bool CrossDN = false, // Flag indicating the downward crossover of the local trend CrossUP = false, // Flag indicating the upward crossover of the local trend CrossGDN = false, // Flag indicating the downward crossover of the global trend CrossGUP = false; // Flag indicating the upward crossover of the global trend double pt=MarketInfo(Symbol(),MODE_POINT); // Point size in the quote currency
そして、下部の極値の特定のためのコードを追加します:
//==================================================================================================== // --------------------------- Identification of lower DN minimum and maximum ------------- //==================================================================================================== PivotTimeDN = TimeDN[pbar]; // Time of the main extremum PivotBarDN = iBarShift(sy,tf,TimeDN[pbar]); // Bar of the main extremum PivotPeakDN = PeakDN[pbar]; // Value of the main extremum LowestPeakDN = ArrayMin(PeakDN); // Get the lower minimum value Index = ArraySearchDouble(PeakDN, LowestPeakDN);// Get the index of the lower minimum in the array LowestBarDN =iBarShift(sy,tf,TimeDN[Index]); // Get the bar of the lower minimum LowestTimeDN = TimeDN[Index]; // Get the time of the lower minimum
メイン極値のバーをpbar変数にて設定し、メイン極値の時間はこの変数にふくまれているインデクスによってその時間配列から取得されます。
PivotTimeDN = TimeDN[pbar]; // Time of the main extremum
メイン極値のバーは、時間配列から取得され、その値は標準関数int iBarShift( string symbol, int timeframe, datetime time, bool exact=false)を用いて計算されます。
それは、極値の配列の最小値の特定を考慮する際に後ほど簡単に紹介されます。
メイン極値の値は、pbar変数を用いて必要な値を明記する値配列から取得されます(pbarはメイン極値のインデックスを含みます。)
PivotPeakDN = PeakDN[pbar]; // Value of the main extremum
下部の最小値を特定するために、標準関数int ArrayMinimum(double array[], int count=WHOLE_ARRAY, int start=0) を用いることができ、それはユーザーによって定義された関数の呼び出しによって実行されるようにその計算を調整します。その関数は極値の特定結果を返します。
全体を再度作成しないために、Igor Kimによって彼のフォーラムへの投稿にてその他の人が使用できるように提供された完成済みの関数を用いてその配列の最小の要素を特定します。それは今回のニーズを完璧に満たし、その配列が何も持たない場合、関連するエラーレポートをログに送信します。
//+----------------------------------------------------------------------------+ //| Author : Igor B. Kim aka KimIV, http://www.kimiv.ru | //+----------------------------------------------------------------------------+ //| Version : 17.05.2008 | //| Description : Returns the value of the minimum element in the array. | //+----------------------------------------------------------------------------+ //| Parameters: | //| x - array of values of numerical series | //+----------------------------------------------------------------------------+ double ArrayMin(double& x[]) { if (ArraySize(x)>0) return(x[ArrayMinimum(x)]); else { Print("ArrayMin(): Array empty!"); return(0); } }
ご覧の通り、何も難しいことはありません - それは同じ標準関数ArrayMinimum()でm渡されるその配列にて必要なデータをチェックします。同じところから取得される類似する関数がその値によってその配列の最小の要素のインデックスを取得するために用いられます:
//+----------------------------------------------------------------------------+ //| Author : Igor B. Kim aka KimIV, http://www.kimiv.ru | //+----------------------------------------------------------------------------+ //| Version : 01.09.2005 | //| Description : Finds an array element by its value | //| and returns the index of the found element or -1. | //+----------------------------------------------------------------------------+ //| Parameters: | //| m - array of elements | //| e - value of the element | //+----------------------------------------------------------------------------+ int ArraySearchDouble(double& m[], double e) { for (int i=0; i<ArraySize(m); i++) { if (m[i]==e) return(i); } return(-1); }
ここで、その関数に渡されるその値を探すため全体の配列をループします。もしその関数に渡される値が配列の要素の値に一致していれば、その関数は、その配列の求められている値のインデックスに等しいその変数iの値を返します。もしその求められている値が見つからなければ、その関数はプログラムのエラーをチェックするために用いられる-1を返します。
その特定された極値に一致するチャートのバーを見つける必要があります。この目的のために、以前格納された時間配列TimeDN[]のデータと取得した必要な極値のインデックス (Index) を用います。
// Get the bar of the lower minimum LowestBarDN =iBarShift(sy,tf,TimeDN[Index]);
このため、標準関数 int iBarShift( string symbol, int timeframe, datetime time, bool exact=false)を用います。
それにより、時間によってそのバーを見つけます。その関数は特定の時間を持つバーの情報を返します。もし特定の時間におけるバーがなければ、 exactパラメーターに依存するその関数は、-1か、最も近いバーの値を返します。
ここで、その関数に渡される最初のパラメーターは、sy変数に保存されたそのチャートの現在のシンボルであり、2番目に渡されるパラメーターは、 tf変数に保存された現在のタイムフレームで、 iBarShift()に渡される datetime timeは、時間配列 TimeDN[] に保存され、以前のステップ中に取得された変数Indexの値によってインデックス化されます。
bool exactの標準値は残しておきます。というのもそのバーがない場合に trueを渡すと、その関数は-1 を返し、その後計算に使用され、エラーにつながるからです。標準値false は、バーが履歴にない場合に、その関数を最も近いバーの値を返すようにさせます。完全に間違った値よりかはましだと思います。
今後の計算や全体の仕組みを理解するたえに、上記で記載されたバーの時間を保存します。これはとてもシンプルです;TimeDN[]配列にて時間を保存しました。そして、同じインデックスを用いこの配列から時間を取得します。
// Get the time of the lower minimum LowestTimeDN = TimeDN[Index];
これでメインの極値と最小値を特定しました。二つの特定されたチャート地点をつなぐトレンド線が二つの特定された極値の間に位置するその他の極値によって交差されるかどうか見る必要があります。
そのような交差はメイン極値に最小値を通って伸びる仮想の線を用いて特定します。最小値からメイン値に向けて極値の配列をループし、その極値と線の間の交差ポイントが見つかれば、その線の開始ポイントとして扱われ、配列全体を再度ループし、メインの極値として特定されたその値から開始します。
その線を交差するその極値は、残るものがなくなるまで稼働されます。これは、必要な極値を特定し、メイン極値へ続く極値はトレンド線を交差しないということを意味します。
if (LowestBarDN>PivotBarDN) // If the minimum is to the right of the first extremum ... // ... (being in 0 cell of the trough time array) for (m=Index-1; m>pbar; m--) // Loop from the extremum following the minimum to the first extremum { // --------- Draw a virtual trend line and check it for crossovers with other extrema ----- CheckCross=EquationDirect(iBarShift(sy,tf,TimeDN[m+1]), PeakDN[m+1], // First coordinate of the line PivotBarDN, PivotPeakDN, // Second coordinate of the line iBarShift(sy,tf,TimeDN[m],false));// Crossing point associated with the next extremum if (TempIND[iBarShift(sy,tf,TimeDN[m])]<CheckCross) // If the extremum lies below the line { if (PeakDN[m]<PeakDN[pbar]) // if this extremum is lower than the main extremum { LowestBarDN =iBarShift(sy,tf,TimeDN[m]); // Then this is the one we need to start from... LowestPeakDN = PeakDN[m]; // New coordinates of the next extremum LowestTimeDN = TimeDN[m]; } } }
ここで、極値の配列の最小値、そして次の極値から最初の極値までのループを調整しました。これは、交差線のチェックが次の極値から開始する際に線を描画する場合、最小値がその開始地点になるために行われます。
その仮想の線は、その直線と交差するポイントでの X におけるY の値を計算する直線等式の関数を用いて描画されます:
double EquationDirect(double x1, double y1, double x2, double y2, double x) { if (x2==x1) return(y1); return((y2-y1)/(x2-x1)*(x-x1)+y1);
x1とy1は、最初のポイントの座標です; x2とy2は、2番目のポイントの座標です ; x は、yが計算されるためのああいです。この関数はI. Kimの フォーラムの投稿のものです。
..:
CheckCross=EquationDirect(iBarShift(sy,tf,TimeDN[m+1]), PeakDN[m+1], // First coordinate of the line PivotBarDN, PivotPeakDN, // Second coordinate of the line iBarShift(sy,tf,TimeDN[m],false)); // Crossing point associated with the next extremum
その最小値のバーを最初のポイントのx-の座標として、最小値の値を最初のポイントのy-座標として、渡されるその関数のパラメーターに挿入してください。そのバーとメイン極値の値は、仮想線のポイントの2番目の座標として用いられます。
その関数に渡される最後の値は、y-座標の値の計算や特定のポイントに位置するその極値と結果の値を比較するための関数にループにて渡されるバーの番号です。
if (TempIND[iBarShift(sy,tf,TimeDN[m])]<CheckCross) // If the extremum lies below the line { if (PeakDN[m]<PeakDN[pbar]) // if this extremum is lower than the main extremum { LowestBarDN =iBarShift(sy,tf,TimeDN[m]); // Then this is the one we need to start from... LowestPeakDN = PeakDN[m]; // New coordinates of the next extremum LowestTimeDN = TimeDN[m]; } }
従って、その関数によって渡されるその値は、特定のポイントに位置する極値よりも大きい場合、その極値は仮想線を交差し、その線はこの極値から開始します。新しい座標の値を保存し、次の極値との交差を特定するために次のループに進みましょう。
その線がさらなる極値によって交差されない極値を特定すれば、そのトレンド線の描画のために使用されます。この極値がメイン極値よりも小さいか、そして、それが、メイン極値に向かって描画されるその線が右の方向に上がっていくことを意味するメイン極値の左にあるかどうかのみ知っています。
if (LowestBarDN>PivotBarDN && LowestPeakDN<PivotPeakDN)// If the minimum is to the left of and below the main extremum
そして、それは、 A/Dチャートに上昇トレンド線を描くために必要なデータをすべて持っていることを意味します。
5. A/Dチャートにトレンド線を描く
メインチャートウィンドウにA/Dインジケーターウィンドウがあるかを特定するために、チャートサブウィンドウ番号を保存する別の変数WinID A/D を紹介します。
WinID=WindowFind("A/D"); // AD window number
その標準関数WindowFind("name");は、特定の名前 nameを持つインジケーターを持つチャートサブウィンドウがあれば、そのの数を返します;なければ、-1を返します。計算に使用しないので、A/Dチャートにトレンド線を描く必要は基本的にありません;しかし、明確にそれを描画し、すべてが正しいことを確認します。
if (WinID>0) { if (ObjectFind("Trend_GLine_DN")<0) ObjectCreate("Trend_GLine_DN",OBJ_TREND,WinID,LowestTimeDN,LowestPeakDN,PivotTimeDN,PivotPeakDN); ObjectSet("Trend_GLine_DN",OBJPROP_COLOR,Lime); ObjectSet("Trend_GLine_DN",OBJPROP_TIME1,LowestTimeDN); ObjectSet("Trend_GLine_DN",OBJPROP_PRICE1,LowestPeakDN); ObjectSet("Trend_GLine_DN",OBJPROP_TIME2,PivotTimeDN); ObjectSet("Trend_GLine_DN",OBJPROP_PRICE2,PivotPeakDN); }
ここで、まずA/Dインジケータウィンドウがメインウィンドウにあるかチェックします。なければ、何も描画されません:
if (WinID>0)
それはシンプルです;もしそのインジケーターがそのチャートウィンドウになければ、そのWindowFind() 関数は-1を返します。そのメインチャートウィンドウは、常に0の値を持ち、私たちはその値が”0よりも大きい”かをチェックします。A/Dチャートとは別に、チャートウィンドウにその他のインジケーターがあるため、確実にどの数が必要なA/Dに割り当てられるか知ることができないため、結果としてその数をチェックせず、その値が0よりも大きいかをチェックします。
さらにもしそのウィンドウ識別子 (WinID) が0よりも大きければ、 その A/D チャートはそのウィンドウに存在するとみなし、描画に進みます。そして、トレンド線がすでにチャートに描かれているかトレンド線の名前を用いてチェックします;
if (ObjectFind("Trend_GLine_DN")<0)
そして、もしまだなければ、 (そのObjectFind() 関数は-1を返し、そうでなければ、そのA/D ウィンドウインデックスが返されます)、描画を開始できます。このために、以下の関数を用います:
ObjectCreate("Trend_GLine_DN",OBJ_TREND,WinID,LowestTimeDN,LowestPeakDN,PivotTimeDN,PivotPeakDN);
bool ObjectCreate(string name, int type, int window, datetime time1, double price1, datetime time2=0, double price2=0, datetime time3=0, double price3=0) creates an object with a specified name, type and initial coordinates in a specified chart subwindow. そのオブジェクトに関連する座標の数は、種類に応じて1から3になります。そのオブジェクトが完成すれば、その関数は、TRUEを返し、そうでなければFALSEを返します。
詳細に見てみましょう:
- string型nameは、作成のために必要なオブジェクト名の値を取得し - この場合、 "Trend_GLine_DN" (下部の極値をつなげるグローバルトレンド線)です; その関数に渡される次のパラメーターは、 int 型で、オブジェクトの種類は、OBJ_TRENDです...
- int window は、 A/Dインジケーターウィンドウ識別子で、WinINDに保存されます...
その関数に渡される次のパラメーターは、トレンド線を描画するために使用される二つのポイントの座標です。
- datetime time1は、その変数に保存される最小値の最初の座標LowestTDNで、
- double price1は、LowestPeakDN変数に保存される最小値の最初の座標で、
- datetime time2は、HighestTDN変数に保存される最小値の2番目の座標です。
- double price2は、変数HighestPeakDNに保存される最小値の2番目の座標です。
ObjectCreate()関数のその他のパラメーターは、トレンド線の描画に使用されません。
なので、懇談会でそのトレンド線オブジェクトが作成されました。それでは、そのパラメーターのいくつかを修正する必要があります。特に、線の色と座標です。線の色は定数であり、一方その座標は新しい極値のデータが変化するたびに修正される必要があり、トレンド線の描画に新しい座標を使用する必要があります。
ObjectSet("Trend_GLine_DN",OBJPROP_COLOR,Lime);
boolObjectSet( stringname, intprop_id, doublevalue) は、特定のオブジェクトの属性の値を変化させます。もし成功であれば、その関数はTRUEを、そうでなければ FALSEを返します。このコードは、Webの色に用いられる標準色ライムを割り当て、上昇トレンドの色を設定します。
その関数に渡されるパラメーターは以下です:
- string name - 修正される必要のある属性を持つオブジェクトの名前 - "Trend_GLine_DN",
- int prop_id - 修正される必要のあるオブジェクト属性の識別子。まず、色を変更し、識別子はOBJPROP_COLORになります。
- doublevalue - 明記されたオブジェクトの属性の新しい値。こちらがその色の定数ライム が渡されるところになります。
その他のオブジェクトの属性の修正は難しくないと思います - オブジェクトの色の変更に関する例に類似しています:
ObjectSet("Trend_GLine_DN",OBJPROP_TIME1,LowestTimeDN); ObjectSet("Trend_GLine_DN",OBJPROP_PRICE1,LowestPeakDN); ObjectSet("Trend_GLine_DN",OBJPROP_TIME2,PivotTimeDN); ObjectSet("Trend_GLine_DN",OBJPROP_PRICE2,PivotPeakDN);
その極値の最初の座標 (OBJPROP_TIME1)を変更し、その後、その最初の値の座標 (OBJPROP_PRICE1)を変更し、その極値の2番目の座標(OBJPROP_TIME2) とその値の座標 (OBJPROP_PRICE1)を変更します。すべての座標値は、その変数 LowestTimeDN、 LowestPeakDN、PivotTimeDN やPivotPeakDNに記述されます。
その他のトレンド線は A/Dインジケーターチャートに描画されます(インジケーターウィンドウがあるか否かに左右されます)それでは、A/D チャートのその線とインジケーターチャートの極値を用いて描画されたトレンド線との交差点を特定する必要があります。このタスクを考えると、直線等式を用いて交差点を計算することがより簡単であるという結論にいたりました。
6. A/Dチャート線とインジケーターチャート極値を通るトレンド線の交差点の特定
トレンド線が描画される二つのポイントを持ち、A/Dインジケーター線の最初のバーに記述される架空の線を計算する直線等式を使用します。(偽のポジティブ値の数を減らすために最初のバーを用います).
直線等式を用いて計算され、インジケーターの最初のバーに記載されたそのポイントは、交差をチェックするポイントになります。さらに、A/D チャートのこれらの計算されたポイントをマークし、上昇もしくは下降インジケーター線によって交差されたかをチェックします。
もう一つ考えなければならないポイントがあります。そのようなものとして交差点を特定するので、この計算はA/Dインジケーターチャートの2番目のバーも必要とします。もしそのインジケーター線が最初のバーの交差点よりも高く、2番目のバーよりも低いか、同等であれば、上昇交差を持つことになります。従って、その極値に基づき二つの線を計算します。その最初の線は、そのインジケーターの最初のバーに描かれ、2番目の線は2番目のバーに描かれます。
仮想交差ポイントは、計算の結果のよいよい視覚化のためにマークされます。
それでは始めましょう..
7. 可能な交差ポイントの特定とマーキング
可能な交差ポイントの計算された値を保存する追加の変数が必要です。
すでに関数の変数リストにそれらを追加しました;
// ------------------------------------------------------------------------------------------ // -------------------------------- Function variables ---------------------------- // ------------------------------------------------------------------------------------------ double PeakUP[], PeakDN[], // Declare peak/trough arrays yUP, yDN, yGUP, yGDN, // Coordinates of the crossing point between the trend line and indicator UP and DN on the 1st bar yUP2, yDN2, yGUP2, yGDN2, // Coordinates of the crossing point between the trend lines and indicator UP and DN on the 2nd bar LowestPeakDN,HighestPeakDN, // Values of the lower minimum and maximum LowestPeakUP,HighestPeakUP; // Values of the upper minimum and maximum datetime TimeDN[], TimeUP[], // Arrays for storing bars of extrema LowestTimeDN, HighestTimeDN, // Bar of the lower minimum and maximum LowestTimeUP, HighestTimeUP, // Bar of the upper minimum and maximum LowestTDN, HighestTDN, // Time of the lower minimum and maximum LowestTUP, HighestTUP; // Time of the upper minimum and maximum int i, k, Index, // "Internal" variables WinID=WindowFind("A/D"); // AD window number
4の代わりに、8つの変数を追加しました。別の4つ変数は、"lローカル"トレンド線や交差ポイントの計算に必要となるためです。yGUP, yGDN, yGUP2 and yGDN2を用い、以下の交差ポイントを保存します。
- 最初のバーの”グローバル”上昇線yGUP ; yGUP2 - 2番目のバーと
- 最初のバーの”グローバル”下降線yGDN ; yGDN2 - 2番目のバー上。
Igor Kimの直線等式関数を再度使いましょう::
//+----------------------------------------------------------------------------+ //| Author : Igor B. Kim aka KimIV, http://www.kimiv.ru | //+----------------------------------------------------------------------------+ //| Version : 12.10.2007 | //| Description : Straight line equation. Calculates the Y value for X | //| at the point of crossing with the straight line. | //+----------------------------------------------------------------------------+ //| Parameters: | //| x1,y1 - coordinates of the first point, | //| x2,y2 - coordinates of the second point, | //| x - value for which Y needs to be calculated | //+----------------------------------------------------------------------------+ double EquationDirect(double x1, double y1, double x2, double y2, double x) { if (x2==x1) return(y1); return((y2-y1)/(x2-x1)*(x-x1)+y1); }
5つのパラメーターが計算の開始点として関数に渡されます;その二つのチャート極値の座標のための二つと交差点が見つかるバーである最後のパラメーターです。
yGDN =EquationDirect(LowestBarDN,LowestPeakDN,PivotBarDN,PivotPeakDN,iBarShift(sy,tf,Time[1],false)); yGDN2=EquationDirect(LowestBarDN,LowestPeakDN,PivotBarDN,PivotPeakDN,iBarShift(sy,tf,Time[2],false));
最小値の座標が最初のポイントの座標として使用されています: x1 = LowestTimeDN, y1 = LowestPeakDN、一方、メイン極値の座標は、2番目のポイントの座標として用いられています: x2 = PivotBarDN, y2 = PivotPeakDN.
必要なバーのシフトは、その等式が計算する、値として渡されます: iBarShift(sy, tf, Time[1], false), sy は、現在の証券のシンボル名で、 tf はタイムフレーム、 Time[1]は現在のチャートのすべてのバーのオープン時間を含むタイムシリーズの配列です。datetime データ型は、1970年1月1日 00:00からの経過秒を示します。
この関数の残りのパラメーターは以前紹介されています。もしその関数がマルチ通貨EAにて使用されれば、 Time[1] の代わりに、現在の証券の値や使用されているタイムフレームを取得する標準関数 iTime() (これは、この関数の最終バージョンにて実装するものです) を使用し、一方Time[] は現在のチャートのみを扱います。
その変数にて計算され保存された交差水準があります。それらは明確化のためA/Dインジケーターチャートにてマークされることができます。
if (WinID>0) { if (ObjectFind("PointGDN"+Time[1])<0) ObjectCreate("PointGDN"+Time[1],OBJ_ARROW,WinID,Time[1],yGDN); ObjectSet("PointGDN"+Time[1],OBJPROP_ARROWCODE,4); ObjectSet("PointGDN"+Time[1],OBJPROP_COLOR,Lime); ObjectSet("PointGDN"+Time[1],OBJPROP_TIME1,Time[1]); ObjectSet("PointGDN"+Time[1],OBJPROP_PRICE1,yGDN); }
これは、トレンド線を描画する際にあるものに類似しています。わずかな違いを見てみましょう。
その最初の違いは、オブジェクト名にあります。
ObjectCreate("PointGDN"+Time[1],OBJ_ARROW,WinID,Time[1],yGDN);
シングルオブジェクトではなく、オブジェクトすべてを新しいバーに作成していくというわけではないので、すべてのオブジェクト名はユニークである必要があります。このため、オブジェクトが生成されるバーの時刻がオブジェクト名に追加されます:
"PointGDN"+Time[1]
2番目の違いはグラフィックスオブジェクト型の識別子にあります。トレンド線の描画のためにOBJ_TREND使用しましたが、矢印(シンボル)を描くOBJ_ARROWを使用します。さらに、WinID は、オブジェクトが描画されるサブウィンドウの数で、この場合、これは、A/Dウィンドウの数であり、二つの座標があります - 時間の座標Time[1] は、最初のバーの時間であり、価格の座標yGDN はトレンド線と計算される最初のバーの交差ポイントです。
そのオブジェクト属性は以下のように設定されます:
ObjectSet("PointGDN"+Time[1],OBJPROP_ARROWCODE,4);
オブジェクト属性の識別子OBJPROP_ARROWCODEは、オブジェクトのコードを設定し、wingdingsシンボルの一つか、事前に定義されている矢印のコードの一つになります。計算された水準でそのオブジェクトを正確に設定するために、正確な価格と時間を指す矢印コードがあります。ハイフン (–)を用います残りの行は、オブジェクトの色やそれぞれのバーの断続的に変わる座標を設定します。
ObjectSet("PointGDN"+Time[1],OBJPROP_COLOR,Lime); ObjectSet("PointGDN"+Time[1],OBJPROP_TIME1,Time[1]); ObjectSet("PointGDN"+Time[1],OBJPROP_PRICE1,yGDN);
インジケーター線と描画された極値に基づき描かれた”グローバル”上昇トレンド線の起こりうる交差ポイントを得ることができました。それは、交差のチェックのためにこれから必要となるポイントです。もし交差が見つかれば、そのシグナルインジケーター - 下矢印 - がメインチャートに設定されます。
8. A/D線の交差と、計算された交差ポイントのチェックと、シグナルインジケーターの設定
A/Dインジケーター線の交差とその交差ポイントが存在するか特定するために、より大きいか、小さいかそれらの値を比較し、もし特定のバーのインジケーター線の値が交差ポイント値よりも小さく、一方前のバーのインジケーター値が交差ポイント値よりも大きければ、下降交差を扱わなければなりません。
if (NormalizeDouble(TempIND[1],8) < NormalizeDouble(yGDN,8) && NormalizeDouble(TempIND[2],8) >= NormalizeDouble(yGDN2,8)) { CrossGDN = true; // Set the flag indicating the downward crossover CrossGUP = false; // Remove the flag indicating the upward crossover if (ObjectFind("ArrowGDN"+Time[1])<0) ObjectCreate("ArrowGDN"+Time[1],OBJ_ARROW, 0 ,Time[1], iHigh(sy,tf,1)+5*pt); ObjectSet("ArrowGDN"+Time[1],OBJPROP_ARROWCODE,242); ObjectSet("ArrowGDN"+Time[1],OBJPROP_COLOR,OrangeRed); ObjectSet("ArrowGDN"+Time[1],OBJPROP_WIDTH,1); ObjectSet("ArrowGDN"+Time[1],OBJPROP_TIME1,Time[1]); ObjectSet("ArrowGDN"+Time[1],OBJPROP_PRICE1,iHigh(sy,tf,1)+5*pt); }
比較のために、Double型の値が標準関数 double NormalizeDouble(double value, int digits)によって、必要な正確さに統一しなければなりません。浮遊点は、整数にし、必要な正確さを保ちます。
計算されたStopLossとTakeProfit値は、未決注文のオープン価格と同様にすでに定義されているDigits変数に設定されているように正規化されなければなりません。
if (NormalizeDouble(TempIND[1],8) < NormalizeDouble(yGDN,8) && NormalizeDouble(TempIND[2],8) >= NormalizeDouble(yGDN2,8))
それは、インジケーター線の値と計算される交差ポイントの値を比較する際にちょうど行ったことです。もしこの条件が満たされれば、bool 型 CrossGDN とCrossGUP の変数は、true とfalse の割り当てられた値です。
そのコードの次のブロックは、ターミナルのメインチャートウィンドウにて下矢印を設定します;
if (ObjectFind("ArrowGDN"+Time[1])<0) ObjectCreate("ArrowGDN"+Time[1],OBJ_ARROW, 0 ,Time[1], iHigh(sy,tf,1)+5*pt); ObjectSet("ArrowGDN"+Time[1],OBJPROP_ARROWCODE,242); ObjectSet("ArrowGDN"+Time[1],OBJPROP_COLOR,OrangeRed); ObjectSet("ArrowGDN"+Time[1],OBJPROP_WIDTH,1); ObjectSet("ArrowGDN"+Time[1],OBJPROP_TIME1,Time[1]); ObjectSet("ArrowGDN"+Time[1],OBJPROP_PRICE1,iHigh(sy,tf,1)+5*pt);
このコードのブロックは、マークや線がターミナルのウィンドウにどのようにセットできるかすでに議論したので、特別な明記は必要としません。以前考察されてない唯一の違いは、ターミナルのメインチャートウィンドウの明記です。このウィンドウの数は、常に0で、ウィンドウ識別子として0を以下の行に設定します。
ObjectCreate("ArrowGDN"+Time[1],OBJ_ARROW, 0 ,Time[1], iHigh(sy,tf,1)+5*pt);
ターミナルのメインウィンドウに矢印を設定できるようにします。
注意すべき数点:バーの上部の最大の価格値は、下矢印のy-座標として機能し(そのコードは242)、そのインデントが5ポイントに設定されます:
iHigh(sy,tf,1)+5*pt
double iHigh( string symbol, int timeframe, int shift) は、関連するチャート (symbol, timeframe)のバーのパラメーターshift によって明記された最大の価格値を返します。
pt の値は、その関数の変数の宣言に後すぐに取得されます。
double pt=MarketInfo(Symbol(),MODE_POINT);
これは標準関数double MarketInfo( string symbol, int type)です。それは、"Market Watch"ウィンドウにリスト化された金融証券の様々な情報を返します。関数クエリ識別子 MODE_POINTは、その通貨のポイントのサイズを返します。現在の証券のために、それはすでに定義されいてる変数Pointに保存されます。:
ObjectSet("ArrowGDN"+Time[1],OBJPROP_WIDTH,1);
表示されている矢印のサイズを 1に設定します。
9. 通貨ペアの価格移動方向における上昇トレンド線の交差のチェック
上昇トレンド線の下降交差は、通貨ペアの価格移動はすぐさま変化することを意味し、同じ上昇トレンド線の交差はその価格が同じ方向で移動し続けることを示します。従って、この種類の交差をチェックし、同時に必要であれば、そのチェックを停止します。
このために、その関数に渡される別の二つのパラメーターに追加することでその関数の最初の行を変えます。
int SignalCrossIND(double &TempIND[], int nBars, string sy, int tf, bool local=false, bool add=false)
こちらでbool type: local とaddの二つの変数を追加しました。その変数 local は、”ローカル”トレンド線を表示するために必要な計算に関わる一方、その変数add は、上昇トレンド線の上向きの交差と下降トレンド線の下向き交差のチェックを行います。これらの変数はfalseに設定され、もしその関数を呼ぶ際に省略されれば、その関数は計算を行わず、ローカルトレンド線を表示せず、交差のチェックは行いません。
if (add) { if (NormalizeDouble(TempIND[1],8) > NormalizeDouble(yGDN,8) && NormalizeDouble(TempIND[2],8) <= NormalizeDouble(yGDN2,8)) { CrossGUP = true; CrossGDN = false; if (ObjectFind("ArrowGUP"+Time[1])<0) ObjectCreate("ArrowGUP"+Time[1],OBJ_ARROW, 0 ,Time[1], iLow(sy,tf,1)); ObjectSet("ArrowGUP"+Time[1],OBJPROP_ARROWCODE,241); ObjectSet("ArrowGUP"+Time[1],OBJPROP_COLOR,Lime); ObjectSet("ArrowGUP"+Time[1],OBJPROP_WIDTH,0); ObjectSet("ArrowGUP"+Time[1],OBJPROP_TIME1,Time[1]); ObjectSet("ArrowGUP"+Time[1],OBJPROP_PRICE1,iLow(sy,tf,1)); } }
このコードブロックはすべて慣れ親しんでいるもので、基本的に詳細に見る必要がありません。注意する必要があるものはその変数addのチェックです:
if (add)
これは以下に同一のものです:
if (add==true)
この変数が"true"に設定されているかチェックします。もし"true"であれば、括弧{}で囲まれるすべての行が実行されます。あと残り二行を考察します。
ObjectSet("ArrowGUP"+Time[1],OBJPROP_ARROWCODE,241);
その関数に渡される最後のパラメーターは、上矢印のコード - 241、そして、
ObjectSet("ArrowGUP"+Time[1],OBJPROP_WIDTH,0)で、;
その関数に渡される最後の値は矢印の描画されている矢印のサイズを意味します。この場合、サイズ0の小さい矢印を表示します。
現在あるコードは、機能的に完全です:A/Dインジケーターデータを読み、その配列を格納するために使用し、インジケーターチャート極値を特定し、そのチャートの上昇トレンド線を描画し、この線とA/Dインジケーター線の交差をチェックし、もしそのような交差が特定されれば、そのターミナルのウィンドウに位置する通貨の証券メインチャート矢印を(交差の方向に基づいて)上か下に向けます。
以下は、類似したチェックのためのコードですが、今回は下降トレンド線のコードです。こちらが分析のためのコードです:
//==================================================================================================== // -------------------- Identification of upper UP minimum and maximum -------------------- //==================================================================================================== PivotTimeUP = TimeUP[pbar]; // Time of the main extremum PivotBarUP = iBarShift(sy,tf,TimeUP[pbar]); // Bar of the main extremum PivotPeakUP = PeakUP[pbar]; // Value of the main extremum HighestPeakUP = ArrayMax(PeakUP); // Get the upper maximum value Index = ArraySearchDouble(PeakUP, HighestPeakUP); // Get the index of the upper maximum in the array HighestBarUP =iBarShift(sy,tf,TimeUP[Index]); // Get the bar of the upper maximum HighestTimeUP = TimeUP[Index]; // Get the time of the upper maximum if (HighestBarUP>PivotBarUP) // If the maximum is to the left of the first extremum ... // ... (being in 0 cell of the peak time array) for (m=Index-1; m>pbar; m--) // Loop from the extremum following the maximum to the first extremum { // --------- Draw a virtual trend line and check it for crossovers with other extrema ---------- CheckCross=EquationDirect(iBarShift(sy,tf,TimeUP[m+1]), PeakUP[m+1], // First coordinate of the line PivotBarUP, PivotPeakUP, // Second coordinate of the line iBarShift(sy,tf,TimeUP[m],false));// Crossing point associated with the next extremum if (TempIND[iBarShift(sy,tf,TimeUP[m])]>CheckCross) // If the extremum lies above the line { if (PeakUP[m]>PeakUP[pbar]) // if this extremum is higher the main extremum { HighestBarUP =iBarShift(sy,tf,TimeUP[m]); // Then this is the one we need to start from... HighestPeakUP = PeakUP[m]; // New coordinates of the next extremum HighestTimeUP = TimeUP[m]; } } } if (HighestBarUP>PivotBarUP && HighestPeakUP>PivotPeakUP) // If the maximum is to the left of the main extremum { // ---------------- Draw a downtrend line (UP - extrema) --------------------- if (WinID>0) { if (ObjectFind("Trend_Line_GUP")<0) ObjectCreate("Trend_Line_GUP",OBJ_TREND,WinID,HighestTimeUP,HighestPeakUP,PivotTimeUP,PivotPeakUP); ObjectSet("Trend_Line_GUP",OBJPROP_COLOR,OrangeRed); ObjectSet("Trend_Line_GUP",OBJPROP_TIME1,HighestTimeUP); ObjectSet("Trend_Line_GUP",OBJPROP_PRICE1,HighestPeakUP); ObjectSet("Trend_Line_GUP",OBJPROP_TIME2,PivotTimeUP); ObjectSet("Trend_Line_GUP",OBJPROP_PRICE2,PivotPeakUP); } //---------------- Calculation of levels of the downtrend (UP - extrema) ------------------ yGUP =EquationDirect(HighestBarUP, HighestPeakUP, PivotBarUP, PivotPeakUP, iBarShift(sy,tf,Time[1],false)); yGUP2=EquationDirect(HighestBarUP, HighestPeakUP, PivotBarUP, PivotPeakUP, iBarShift(sy,tf,Time[2],false)); //---------------- Draw levels of the downtrend (UP - extrema) ------------------ if (WinID>0) { if (ObjectFind("PointGUP"+Time[1])<0) ObjectCreate("PointGUP"+Time[1],OBJ_ARROW,WinID,Time[1],yGUP); ObjectSet("PointGUP"+Time[1],OBJPROP_ARROWCODE,4); ObjectSet("PointGUP"+Time[1],OBJPROP_COLOR,OrangeRed); ObjectSet("PointGUP"+Time[1],OBJPROP_TIME1,Time[1]); ObjectSet("PointGUP"+Time[1],OBJPROP_PRICE1,yGUP); } // --------------- Check for an upward crossover of the downtrend ----------------- if (NormalizeDouble(TempIND[1],8) > NormalizeDouble(yGUP,8) && NormalizeDouble(TempIND[2],8) <= NormalizeDouble(yGUP2,8)) { CrossGUP = true; // Set the flag indicating the upward crossover CrossGDN = false; // Remove the flag indicating the downward crossover if (ObjectFind("ArrowGUP"+Time[1])<0) ObjectCreate("ArrowGUP"+Time[1],OBJ_ARROW, 0 ,Time[1], iLow(sy,tf,1)); ObjectSet("ArrowGUP"+Time[1],OBJPROP_ARROWCODE,241); ObjectSet("ArrowGUP"+Time[1],OBJPROP_COLOR,Lime); ObjectSet("ArrowGUP"+Time[1],OBJPROP_WIDTH,1); ObjectSet("ArrowGUP"+Time[1],OBJPROP_TIME1,Time[1]); ObjectSet("ArrowGUP"+Time[1],OBJPROP_PRICE1,iLow(sy,tf,1)); } // --------------- Check for a downward crossover of the downtrend ----------------- if (add) { if (NormalizeDouble(TempIND[1],8) < NormalizeDouble(yGUP,8) && NormalizeDouble(TempIND[2],8) >= NormalizeDouble(yGUP2,8)) { CrossGDN = true; CrossGUP = false; if (ObjectFind("ArrowGDN"+Time[1])<0) ObjectCreate("ArrowGDN"+Time[1],OBJ_ARROW, 0 ,Time[1], iHigh(sy,tf,1)+15*pt); ObjectSet("ArrowGDN"+Time[1],OBJPROP_ARROWCODE,242); ObjectSet("ArrowGDN"+Time[1],OBJPROP_COLOR,OrangeRed); ObjectSet("ArrowGDN"+Time[1],OBJPROP_WIDTH,0); ObjectSet("ArrowGDN"+Time[1],OBJPROP_TIME1,Time[1]); ObjectSet("ArrowGDN"+Time[1],OBJPROP_PRICE1,iHigh(sy,tf,1)+15*pt); } } }
10. A/Dチャートでの"ローカル"極値の特定、特定された極値を通るトレンド線の描画、交差のチェック
あとは、”ローカル”極値における同様の調整を行う必要があります。
”グローバル”極値とは異なる点として、それらの特定の方法が異なります。最も右の二つの極値を用いて特定され、もしその最初の極値が最初の極値の左に位置する2番目のものよりも高い場合、ローカルトレンド線が描かれると考えます。下降トレンドにおいて、その特定された2番目の極値はより高い、最初の極値の左に位置する必要があります。
そのコードは、上記ですでに見たものと完全に同一であるので(実際、数点以外はほぼ同一です)、上昇トレンド線の全コードを記載します:
// --------------------------- Identification of two (local) outermost lower DN extrema ------------ if (local) { asize=ArraySize(PeakDN); for (l=asize-1; l>=1; l--) { if (PeakDN[l]<PeakDN[l-1]) { LastTimeDN =TimeDN[l-1]; LastVarDN =PeakDN[l-1]; PreLastTimeDN =TimeDN[l]; PreLastVarDN =PeakDN[l]; } } // --------------- Draw a local uptrend line (DN - extrema) ----------------- if (WinID>0) { if (ObjectFind("Trend_Line_DN")<0) ObjectCreate("Trend_Line_DN",OBJ_TREND,WinID,TimeDN[1],PeakDN[1],TimeDN[0],PeakDN[0]); ObjectSet("Trend_Line_DN",OBJPROP_COLOR,MediumSeaGreen); ObjectSet("Trend_Line_DN",OBJPROP_TIME1,PreLastTimeDN); ObjectSet("Trend_Line_DN",OBJPROP_PRICE1,PreLastVarDN); ObjectSet("Trend_Line_DN",OBJPROP_TIME2,LastTimeDN); ObjectSet("Trend_Line_DN",OBJPROP_PRICE2,LastVarDN); } //---------------- Calculation of levels of the local uptrend (DN - extrema) ---------------- yDN =EquationDirect(iBarShift(sy,tf,PreLastTimeDN,false), PreLastVarDN, iBarShift(sy,tf,LastTimeDN,false), LastVarDN, iBarShift(sy,tf,Time[1],false)); yDN2=EquationDirect(iBarShift(sy,tf,PreLastTimeDN,false), PreLastVarDN, iBarShift(sy,tf,LastTimeDN,false), LastVarDN, iBarShift(sy,tf,Time[2],false)); //---------------- Draw levels of the local uptrend (DN - extrema) ---------------- if (WinID>0) { if (ObjectFind("PointDN"+Time[1])<0) ObjectCreate("PointDN"+Time[1],OBJ_ARROW,WinID,Time[1],yDN); ObjectSet("PointDN"+Time[1],OBJPROP_ARROWCODE,4); ObjectSet("PointDN"+Time[1],OBJPROP_COLOR,MediumSeaGreen); ObjectSet("PointDN"+Time[1],OBJPROP_TIME1,Time[1]); ObjectSet("PointDN"+Time[1],OBJPROP_PRICE1,yDN); } // --------------- Check for a downward crossover of the local uptrend ----------------- if (NormalizeDouble(TempIND[1],8) < NormalizeDouble(yDN,8) && NormalizeDouble(TempIND[2],8) >= NormalizeDouble(yDN2,8)) { CrossDN = true; CrossUP = false; if (Arrow) // If the use of signal indicators is allowed { if (ObjectFind("ArrowDN"+Time[1])<0) ObjectCreate("ArrowDN"+Time[1],OBJ_ARROW, 0 ,Time[1], iHigh(sy,tf,1)+15*pt); ObjectSet("ArrowDN"+Time[1],OBJPROP_ARROWCODE,242); ObjectSet("ArrowDN"+Time[1],OBJPROP_COLOR,Chocolate); ObjectSet("ArrowDN"+Time[1],OBJPROP_WIDTH,1); ObjectSet("ArrowDN"+Time[1],OBJPROP_TIME1,Time[1]); ObjectSet("ArrowDN"+Time[1],OBJPROP_PRICE1,iHigh(sy,tf,1)+15*pt); } } // --------------- Check for an upward crossover of the local uptrend ----------------- if (add) { if (NormalizeDouble(TempIND[1],8) > NormalizeDouble(yDN,8) && NormalizeDouble(TempIND[2],8) <= NormalizeDouble(yDN2,8)) { CrossUP = true; CrossDN = false; if (Arrow) // If the use of signal indicators is allowed { if (ObjectFind("ArrowUP"+Time[1])<0) ObjectCreate("ArrowUP"+Time[1],OBJ_ARROW, 0 ,Time[1], iLow(sy,tf,1)); ObjectSet("ArrowUP"+Time[1],OBJPROP_ARROWCODE,241); ObjectSet("ArrowUP"+Time[1],OBJPROP_COLOR,MediumSeaGreen); ObjectSet("ArrowUP"+Time[1],OBJPROP_WIDTH,0); ObjectSet("ArrowUP"+Time[1],OBJPROP_TIME1,Time[1]); ObjectSet("ArrowUP"+Time[1],OBJPROP_PRICE1,iLow(sy,tf,1)); } } } }
唯一の違いは、そのトレンド線が通る極値の特定です。
if (local)
この行は、もしそのトレンド線の描画やA/Dチャートの交差の計算が可能かチェックします。
そして、ループを回し、最も外側の二つの極値を探します。
asize=ArraySize(PeakDN); for (l=asize-1; l>=1; l--) { if (PeakDN[l]<PeakDN[l-1]) { LastTimeDN =TimeDN[l-1]; LastVarDN =PeakDN[l-1]; PreLastTimeDN =TimeDN[l]; PreLastVarDN =PeakDN[l]; } }
そして、それらの値を変数LastTimeDN、LastVarDN、PreLastTimeDN と PreLastVarDNに記述します。
- LastTimeDNは最後の極値の時刻を含み、
- LastVarDNは、最後の極値の値を含み、
- PreLastTimeDNは、最後から一つの極値の時間を含み、
- PreLastVarDNは、最後から一つの極値の値を含みます。
その後、トレンド線を描画し、それらとの交差の特定に使用される変数を覗いて、そのコードは仮想的に上記ですでに考察されているものと同一です。そして、その関数の値がこの最後のひと仕上げで返されるように調整する必要があります。
これは一般的に以下の通りです:
if (CrossGUP || CrossUP) return(1); else if (CrossGDN || CrossDN) return(-1); else return(0);
上昇トレンド線の下向きの交差があるところにて、その関数は-1を返します;下降トレンド線の下向き交差があるところでは、その関数は1を返します;その他のすべての場合、その関数は0を返します - それはシンプルなものです。
上昇トレンド線と下降トレンド線の全交差をチェックし、交差の組み合わせに応じて、それらに”重み”をつけ、必要な値をそのシグナルの処理のための呼び出し関数に返す方が確実に良いです。
その関数の記載の結論にて、いくつかのマイナーチェンジを含めて、その完全なコードを提示します( A/Dチャートとそのトレンド線の交差が特定された際にシグナルインジケーターを設定される可能かのチェックとその関数の一般性を保証するTime の代わりに iTime()標準関数の使用 に関するものです)
提示されたその広範囲の考察ののち、以下に提示されるそのコードが理解できるものであれば幸いです:
//жжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжж // Identification of a crossover of the trend line and the A/D chart line and setting of the signal indicators //жжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжж // TempIND[] - (declared on the global level) array for storing A/D indicator data // nBars - number of bars for identification of extrema // sy - working symbol: "" or NULL - current symbol of the chart // tf - working time frame: 0 - current, // local - whether to calculate based on local trends: by default - false // add - whether to react to the crossover of the trend in the direction of the trend: by default - false // Arrow - whether to set signal indicators in the currency instrument chart // when a crossover of the trend line and the A/D chart line is identified: by default - true //========================================================================================== int SignalCrossIND(double &TempIND[], int nBars, string sy, int tf, bool local=false, bool add=false, bool Arrow=true) { if (sy=="" || sy=="0") sy = Symbol(); ArrayResize(TempIND,nBars); // Resize the array according to the size passed to the function for (int j=0; j<=nBars-1; j++) { TempIND[j]=iAD(sy,tf,j); // Write the indicator data in the loop into the array } // ========================================================================================== // ------------------------------------ Function variables ---------------------------------+ // ========================================================================================== double PeakUP[], PeakDN[], // Declare peak/trough arrays yUP, yDN, yGUP, yGDN, // Coordinates of the crossing point between the trend line and indicator UP and DN on the 1st bar yUP2, yDN2, yGUP2, yGDN2, // Coordinates of the crossing point between the trend line and indicator UP and DN on the 2nd bar CheckCross, PreLastVarDN,LastVarDN, // Values of the last and last but one lower extrema PreLastVarUP,LastVarUP, // Values of the last and last but one upper extrema PivotPeakDN,PivotPeakUP, LowestPeakDN,HighestPeakDN, // Values of the lower minimum and maximum LowestPeakUP,HighestPeakUP, // Values of the upper minimum and maximum datetime TimeDN[], TimeUP[], // Arrays for storing bars of extrema PreLastTimeDN, LastTimeDN, // Time of the last and last but one lower extrema PreLastTimeUP, LastTimeUP, // Time of the last and last but one upper extrema PivotBarDN, PivotTimeDN, // Bar and time of the lower main extremum PivotBarUP, PivotTimeUP, // Bar and time of the upper main extremum LowestBarDN, LowestTimeDN, // Bar and time of the lower minimum HighestBarUP, HighestTimeUP; // Bar and time of the upper maximum int i, kup, kdn, pbar=2, m, l, t, asize, Index, // "Internal" variables WinID=WindowFind("A/D"); // AD window number bool CrossDN = false, // Flag indicating the downward crossover of the local trend CrossUP = false, // Flag indicating the upward crossover of the local trend CrossGDN = false, // Flag indicating the downward crossover of the global trend CrossGUP = false; // Flag indicating the upward crossover of the global trend double pt=MarketInfo(Symbol(),MODE_POINT); // Point size in the quote currency // ========================================================================================== // ------------------ Filling arrays with data on peaks and troughs ----------------------+ // ========================================================================================== kdn=0; // Initialize the trough array index for (i=2; i<=nBars-1; i++) // Run through the array of values from the 2nd bar deep into history { if (TempIND[i]<TempIND[i-1] && TempIND[i+1]>=TempIND[i]) // Trough identified WAS THERE >= { ArrayResize(PeakDN, kdn+1); // Resize the trough array according to the number of troughs identified ArrayResize(TimeDN, kdn+1); // Resize the trough time array according to the number of troughs PeakDN[kdn]=TempIND[i]; // Write the trough value into the trough array... TimeDN[kdn]=iTime(sy,tf,i); // ...and the time array kdn++; // Increase the trough array index } } // ----------------------------------------------------------------------------------------------------------- kup=0; // Initialize the peak array index for (i=2; i<=nBars-1; i++) // Run through the array of values from the 2nd bar deep into history { if (TempIND[i]>TempIND[i-1] && // WAS THERE > TempIND[i+1]<=TempIND[i]) // Peak identified WAS THERE <= { ArrayResize(PeakUP, kup+1); // Resize the peak array according to the number of peaks identified ArrayResize(TimeUP, kup+1); // Resize the peak time array according to the number of peaks PeakUP[kup]=TempIND[i]; // Write its value into the peak array... TimeUP[kup]=iTime(sy,tf,i); // ...and the time array kup++; // Increase the peak array index } } //==================================================================================================== // --------------------------- Identification of the lower minimum and main DN extremum -----------------+ //==================================================================================================== PivotTimeDN = TimeDN[pbar]; // Time of the main extremum PivotBarDN = iBarShift(sy,tf,TimeDN[pbar]); // Bar of the main extremum PivotPeakDN = PeakDN[pbar]; // Value of the main extremum LowestPeakDN = ArrayMin(PeakDN); // Get the lower minimum value Index = ArraySearchDouble(PeakDN, LowestPeakDN); // Get the index of the lower minimum in the array LowestBarDN =iBarShift(sy,tf,TimeDN[Index]); // Get the bar of the lower minimum LowestTimeDN = TimeDN[Index]; // Get the time of the lower minimum if (LowestBarDN>PivotBarDN) // If the minimum is to the left of the first extremum ... // ... (being in 0 cell of the trough time array) for (m=Index-1; m>pbar; m--) // Loop from the extremum following the minimum to the first extremum { // --------- Draw a virtual trend line and check it for crossovers with other extrema ---------- CheckCross=EquationDirect(iBarShift(sy,tf,TimeDN[m+1]), PeakDN[m+1], // First coordinate of the line PivotBarDN, PivotPeakDN, // Second coordinate of the line iBarShift(sy,tf,TimeDN[m],false));// Crossing point associated with the next extremum if (TempIND[iBarShift(sy,tf,TimeDN[m])]<CheckCross) // If the extremum lies below the line { if (PeakDN[m]<PeakDN[pbar]) // if this extremum is lower than the main extremum { LowestBarDN =iBarShift(sy,tf,TimeDN[m]); // Then this is the one we need to start from... LowestPeakDN = PeakDN[m]; // New coordinates of the next extremum LowestTimeDN = TimeDN[m]; } } } if (LowestBarDN>PivotBarDN && LowestPeakDN<PivotPeakDN) // If the minimum is to the left of and below the main extremum { // --------------- Draw an uptrend line for adjustment (DN - extrema) --------------------- if (WinID>0) { if (ObjectFind("Trend_GLine_DN")<0) ObjectCreate("Trend_GLine_DN",OBJ_TREND,WinID,LowestTimeDN,LowestPeakDN,PivotTimeDN,PivotPeakDN); ObjectSet("Trend_GLine_DN",OBJPROP_COLOR,Lime); ObjectSet("Trend_GLine_DN",OBJPROP_TIME1,LowestTimeDN); ObjectSet("Trend_GLine_DN",OBJPROP_PRICE1,LowestPeakDN); ObjectSet("Trend_GLine_DN",OBJPROP_TIME2,PivotTimeDN); ObjectSet("Trend_GLine_DN",OBJPROP_PRICE2,PivotPeakDN); } //---------------- Calculation of levels of the uptrend (DN - extrema) ---------------- yGDN =EquationDirect(LowestBarDN, LowestPeakDN,PivotBarDN, PivotPeakDN, iBarShift(sy,tf,iTime(sy,tf,1),false)); yGDN2=EquationDirect(LowestBarDN, LowestPeakDN,PivotBarDN, PivotPeakDN, iBarShift(sy,tf,iTime(sy,tf,2),false)); //---------------- Draw levels of the uptrend (DN - extrema) ------------------ if (WinID>0) { if (ObjectFind("PointGDN"+iTime(sy,tf,1))<0) ObjectCreate("PointGDN"+iTime(sy,tf,1),OBJ_ARROW,WinID,iTime(sy,tf,1),yGDN); ObjectSet("PointGDN"+iTime(sy,tf,1),OBJPROP_ARROWCODE,4); ObjectSet("PointGDN"+iTime(sy,tf,1),OBJPROP_COLOR,Lime); ObjectSet("PointGDN"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1)); ObjectSet("PointGDN"+iTime(sy,tf,1),OBJPROP_PRICE1,yGDN); } // --------------- Check for a downward crossover of the uptrend ----------------- if (NormalizeDouble(TempIND[1],8) < NormalizeDouble(yGDN,8) && NormalizeDouble(TempIND[2],8) >= NormalizeDouble(yGDN2,8)) { CrossGDN = true; // Set the flag indicating the downward crossover CrossGUP = false; // Remove the flag indicating the upward crossover if (Arrow) // If the use of signal indicators is allowed { if (ObjectFind("ArrowGDN"+iTime(sy,tf,1))<0) ObjectCreate("ArrowGDN"+iTime(sy,tf,1),OBJ_ARROW, 0 ,iTime(sy,tf,1), iHigh(sy,tf,1)+5*pt); ObjectSet("ArrowGDN"+iTime(sy,tf,1),OBJPROP_ARROWCODE,242); ObjectSet("ArrowGDN"+iTime(sy,tf,1),OBJPROP_COLOR,OrangeRed); ObjectSet("ArrowGDN"+iTime(sy,tf,1),OBJPROP_WIDTH,1); ObjectSet("ArrowGDN"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1)); ObjectSet("ArrowGDN"+iTime(sy,tf,1),OBJPROP_PRICE1,iHigh(sy,tf,1)+5*pt); } } // --------------- Check for an upward crossover of the uptrend ----------------- if (add) { if (NormalizeDouble(TempIND[1],8) > NormalizeDouble(yGDN,8) && NormalizeDouble(TempIND[2],8) <= NormalizeDouble(yGDN2,8)) { CrossGUP = true; CrossGDN = false; if (Arrow) // If the use of signal indicators is allowed { if (ObjectFind("ArrowGUP"+iTime(sy,tf,1))<0) ObjectCreate("ArrowGUP"+iTime(sy,tf,1),OBJ_ARROW, 0 ,iTime(sy,tf,1), iLow(sy,tf,1)); ObjectSet("ArrowGUP"+iTime(sy,tf,1),OBJPROP_ARROWCODE,241); ObjectSet("ArrowGUP"+iTime(sy,tf,1),OBJPROP_COLOR,Lime); ObjectSet("ArrowGUP"+iTime(sy,tf,1),OBJPROP_WIDTH,0); ObjectSet("ArrowGUP"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1)); ObjectSet("ArrowGUP"+iTime(sy,tf,1),OBJPROP_PRICE1,iLow(sy,tf,1)); } } } } //==================================================================================================== // -------------------- Identification of the upper maximum and main UP extremum ----------------------+ //==================================================================================================== PivotTimeUP = TimeUP[pbar]; // Time of the main extremum PivotBarUP = iBarShift(sy,tf,TimeUP[pbar]); // Bar of the main extremum PivotPeakUP = PeakUP[pbar]; // Value of the main extremum HighestPeakUP = ArrayMax(PeakUP); // Get the value of the upper maximum Index = ArraySearchDouble(PeakUP, HighestPeakUP); // Get the index of the upper maximum in the array HighestBarUP =iBarShift(sy,tf,TimeUP[Index]); // Get the bar of the upper maximum HighestTimeUP = TimeUP[Index]; // Get the time of the upper maximum if (HighestBarUP>PivotBarUP) // If the maximum is to the left of the first extremum ... // ... (being in 0 cell of the peak time array) for (m=Index-1; m>pbar; m--) // Loop from the extremum following the maximum to the first extremum { // --------- Draw a virtual trend line and check it for crossovers with other extrema ---------- CheckCross=EquationDirect(iBarShift(sy,tf,TimeUP[m+1]), PeakUP[m+1], // First coordinate of the line PivotBarUP, PivotPeakUP, // Second coordinate of the line iBarShift(sy,tf,TimeUP[m],false)); // Crossing point associated with the next extremum if (TempIND[iBarShift(sy,tf,TimeUP[m])]>CheckCross) // If the extremum lies above the line { if (PeakUP[m]>PeakUP[pbar]) // if this extremum is higher than the main extremum { HighestBarUP =iBarShift(sy,tf,TimeUP[m]); // Then this is the one we need to start from... HighestPeakUP = PeakUP[m]; // New coordinates of the next extremum HighestTimeUP = TimeUP[m]; } } } if (HighestBarUP>PivotBarUP && HighestPeakUP>PivotPeakUP) // If the maximum is to the left of the main extremum { // ---------------- Draw a downtrend line (UP - extrema) --------------------- if (WinID>0) { if (ObjectFind("Trend_Line_GUP")<0) ObjectCreate("Trend_Line_GUP",OBJ_TREND,WinID,HighestTimeUP,HighestPeakUP,PivotTimeUP,PivotPeakUP); ObjectSet("Trend_Line_GUP",OBJPROP_COLOR,OrangeRed); ObjectSet("Trend_Line_GUP",OBJPROP_TIME1,HighestTimeUP); ObjectSet("Trend_Line_GUP",OBJPROP_PRICE1,HighestPeakUP); ObjectSet("Trend_Line_GUP",OBJPROP_TIME2,PivotTimeUP); ObjectSet("Trend_Line_GUP",OBJPROP_PRICE2,PivotPeakUP); } //---------------- Calculation of levels of the downtrend (UP - extrema) ------------------ yGUP =EquationDirect(HighestBarUP, HighestPeakUP, PivotBarUP, PivotPeakUP, iBarShift(sy,tf,iTime(sy,tf,1),false)); yGUP2=EquationDirect(HighestBarUP, HighestPeakUP, PivotBarUP, PivotPeakUP, iBarShift(sy,tf,iTime(sy,tf,2),false)); //---------------- Draw levels of the downtrend (UP - extrema) ------------------ if (WinID>0) { if (ObjectFind("PointGUP"+iTime(sy,tf,1))<0) ObjectCreate("PointGUP"+iTime(sy,tf,1),OBJ_ARROW,WinID,iTime(sy,tf,1),yGUP); ObjectSet("PointGUP"+iTime(sy,tf,1),OBJPROP_ARROWCODE,4); ObjectSet("PointGUP"+iTime(sy,tf,1),OBJPROP_COLOR,OrangeRed); ObjectSet("PointGUP"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1)); ObjectSet("PointGUP"+iTime(sy,tf,1),OBJPROP_PRICE1,yGUP); } // --------------- Check for an upward crossover of the downtrend ----------------- if (NormalizeDouble(TempIND[1],8) > NormalizeDouble(yGUP,8) && NormalizeDouble(TempIND[2],8) <= NormalizeDouble(yGUP2,8)) { CrossGUP = true; // Set the flag indicating the upward crossover CrossGDN = false; // Remove the flag indicating the downward crossover if (Arrow) // If the use of signal indicators is allowed { if (ObjectFind("ArrowGUP"+iTime(sy,tf,1))<0) ObjectCreate("ArrowGUP"+iTime(sy,tf,1),OBJ_ARROW, 0 ,iTime(sy,tf,1), iLow(sy,tf,1)); ObjectSet("ArrowGUP"+iTime(sy,tf,1),OBJPROP_ARROWCODE,241); ObjectSet("ArrowGUP"+iTime(sy,tf,1),OBJPROP_COLOR,Lime); ObjectSet("ArrowGUP"+iTime(sy,tf,1),OBJPROP_WIDTH,1); ObjectSet("ArrowGUP"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1)); ObjectSet("ArrowGUP"+iTime(sy,tf,1),OBJPROP_PRICE1,iLow(sy,tf,1)); } } // --------------- Check for a downward crossover of the downtrend ----------------- if (add) { if (NormalizeDouble(TempIND[1],8) < NormalizeDouble(yGUP,8) && NormalizeDouble(TempIND[2],8) >= NormalizeDouble(yGUP2,8)) { CrossGDN = true; CrossGUP = false; if (Arrow) // If the use of signal indicators is allowed { if (ObjectFind("ArrowGDN"+iTime(sy,tf,1))<0) ObjectCreate("ArrowGDN"+iTime(sy,tf,1),OBJ_ARROW, 0 ,iTime(sy,tf,1), iHigh(sy,tf,1)+15*pt); ObjectSet("ArrowGDN"+iTime(sy,tf,1),OBJPROP_ARROWCODE,242); ObjectSet("ArrowGDN"+iTime(sy,tf,1),OBJPROP_COLOR,OrangeRed); ObjectSet("ArrowGDN"+iTime(sy,tf,1),OBJPROP_WIDTH,0); ObjectSet("ArrowGDN"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1)); ObjectSet("ArrowGDN"+iTime(sy,tf,1),OBJPROP_PRICE1,iHigh(sy,tf,1)+15*pt); } } } } //жжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжж // ========================================================================================== // ------------------ Identification of two (local) outermost lower DN extrema -----------------+ // ========================================================================================== if (local) { asize=ArraySize(PeakDN); for (l=asize-1; l>=1; l--) { if (PeakDN[l]<PeakDN[l-1]) { LastTimeDN =TimeDN[l-1]; LastVarDN =PeakDN[l-1]; PreLastTimeDN =TimeDN[l]; PreLastVarDN =PeakDN[l]; } } // --------------- Draw a local uptrend line (DN - extrema) --------------------- if (WinID>0) { if (ObjectFind("Trend_Line_DN")<0) ObjectCreate("Trend_Line_DN",OBJ_TREND,WinID,TimeDN[1],PeakDN[1],TimeDN[0],PeakDN[0]); ObjectSet("Trend_Line_DN",OBJPROP_COLOR,MediumSeaGreen); ObjectSet("Trend_Line_DN",OBJPROP_TIME1,PreLastTimeDN); ObjectSet("Trend_Line_DN",OBJPROP_PRICE1,PreLastVarDN); ObjectSet("Trend_Line_DN",OBJPROP_TIME2,LastTimeDN); ObjectSet("Trend_Line_DN",OBJPROP_PRICE2,LastVarDN); } //---------------- Calculation of levels of the local uptrend (DN - extrema) ---------------- yDN =EquationDirect(iBarShift(sy,tf,PreLastTimeDN,false), PreLastVarDN, iBarShift(sy,tf,LastTimeDN,false), LastVarDN, iBarShift(sy,tf,iTime(sy,tf,1),false)); yDN2=EquationDirect(iBarShift(sy,tf,PreLastTimeDN,false), PreLastVarDN, iBarShift(sy,tf,LastTimeDN,false), LastVarDN, iBarShift(sy,tf,iTime(sy,tf,2),false)); //---------------- Draw levels of the local uptrend (DN - extrema) ------------------ if (WinID>0) { if (ObjectFind("PointDN"+iTime(sy,tf,1))<0) ObjectCreate("PointDN"+iTime(sy,tf,1),OBJ_ARROW,WinID,iTime(sy,tf,1),yDN); ObjectSet("PointDN"+iTime(sy,tf,1),OBJPROP_ARROWCODE,4); ObjectSet("PointDN"+iTime(sy,tf,1),OBJPROP_COLOR,MediumSeaGreen); ObjectSet("PointDN"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1)); ObjectSet("PointDN"+iTime(sy,tf,1),OBJPROP_PRICE1,yDN); } // --------------- Check for a downward crossover of the local uptrend ----------------- if (NormalizeDouble(TempIND[1],8) < NormalizeDouble(yDN,8) && NormalizeDouble(TempIND[2],8) >= NormalizeDouble(yDN2,8)) { CrossDN = true; CrossUP = false; if (Arrow) // If the use of signal indicators is allowed { if (ObjectFind("ArrowDN"+iTime(sy,tf,1))<0) ObjectCreate("ArrowDN"+iTime(sy,tf,1),OBJ_ARROW, 0 ,iTime(sy,tf,1), iHigh(sy,tf,1)+15*pt); ObjectSet("ArrowDN"+iTime(sy,tf,1),OBJPROP_ARROWCODE,242); ObjectSet("ArrowDN"+iTime(sy,tf,1),OBJPROP_COLOR,Chocolate); ObjectSet("ArrowDN"+iTime(sy,tf,1),OBJPROP_WIDTH,1); ObjectSet("ArrowDN"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1)); ObjectSet("ArrowDN"+iTime(sy,tf,1),OBJPROP_PRICE1,iHigh(sy,tf,1)+15*pt); } } // --------------- Check for an upward crossover of the local uptrend ----------------- if (add) { if (NormalizeDouble(TempIND[1],8) > NormalizeDouble(yDN,8) && NormalizeDouble(TempIND[2],8) <= NormalizeDouble(yDN2,8)) { CrossUP = true; CrossDN = false; if (Arrow) // If the use of signal indicators is allowed { if (ObjectFind("ArrowUP"+iTime(sy,tf,1))<0) ObjectCreate("ArrowUP"+iTime(sy,tf,1),OBJ_ARROW, 0 ,iTime(sy,tf,1), iLow(sy,tf,1)); ObjectSet("ArrowUP"+iTime(sy,tf,1),OBJPROP_ARROWCODE,241); ObjectSet("ArrowUP"+iTime(sy,tf,1),OBJPROP_COLOR,MediumSeaGreen); ObjectSet("ArrowUP"+iTime(sy,tf,1),OBJPROP_WIDTH,0); ObjectSet("ArrowUP"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1)); ObjectSet("ArrowUP"+iTime(sy,tf,1),OBJPROP_PRICE1,iLow(sy,tf,1)); } } } } //==================================================================================================== // ------------------------- Identification of two (local) outermost upper UP extrema ------------------+ //==================================================================================================== if (local) { asize=ArraySize(PeakUP); for (l=asize-1; l>=1; l--) { if (PeakUP[l]>PeakUP[l-1]) { LastTimeUP =TimeUP[l-1]; LastVarUP =PeakUP[l-1]; PreLastTimeUP =TimeUP[l]; PreLastVarUP =PeakUP[l]; } } // ---------------- Draw a local downtrend line (UP - extrema) --------------------- if (WinID>0) { if (ObjectFind("Trend_Line_UP")<0) ObjectCreate("Trend_Line_UP",OBJ_TREND,WinID,TimeUP[1],PeakUP[1],TimeUP[0],PeakUP[0]); ObjectSet("Trend_Line_UP",OBJPROP_COLOR,Chocolate); ObjectSet("Trend_Line_UP",OBJPROP_TIME1,PreLastTimeUP); ObjectSet("Trend_Line_UP",OBJPROP_PRICE1,PreLastVarUP); ObjectSet("Trend_Line_UP",OBJPROP_TIME2,LastTimeUP); ObjectSet("Trend_Line_UP",OBJPROP_PRICE2,LastVarUP); } //---------------- Calculation of levels of the local downtrend (UP - extrema) ------------------ yUP =EquationDirect(iBarShift(sy,tf,PreLastTimeUP,false), PreLastVarUP, iBarShift(sy,tf,LastTimeUP,false), LastVarUP, iBarShift(sy,tf,iTime(sy,tf,1),false)); yUP2=EquationDirect(iBarShift(sy,tf,PreLastTimeUP,false), PreLastVarUP, iBarShift(sy,tf,LastTimeUP,false), LastVarUP, iBarShift(sy,tf,iTime(sy,tf,2),false)); //---------------- Draw levels of the local downtrend (UP - extrema) ------------------ if (WinID>0) { if (ObjectFind("PointUP"+iTimeiTimesy,tf,1))<0) ObjectCreate("PointUP"+iTime(sy,tf,1),OBJ_ARROW,WinID,iTime(sy,tf,1),yUP); ObjectSet("PointUP"+iTime(sy,tf,1),OBJPROP_ARROWCODE,4); ObjectSet("PointUP"+iTime(sy,tf,1),OBJPROP_COLOR,Chocolate); ObjectSet("PointUP"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1)); ObjectSet("PointUP"+iTime(sy,tf,1),OBJPROP_PRICE1,yUP); } // --------------- Check for an upward crossover of the local downtrend ----------------- if (NormalizeDouble(TempIND[1],8) > NormalizeDouble(yUP,8) && NormalizeDouble(TempIND[2],8) <= NormalizeDouble(yUP2,8)) { CrossUP = true; CrossDN = false; if (Arrow) // If the use of signal indicators is allowed { if (ObjectFind("ArrowUP"+iTime(sy,tf,1))<0) ObjectCreate("ArrowUP"+iTime(sy,tf,1),OBJ_ARROW, 0 ,iTime(sy,tf,1), iLow(sy,tf,1)); ObjectSet("ArrowUP"+iTime(sy,tf,1),OBJPROP_ARROWCODE,241); ObjectSet("ArrowUP"+iTime(sy,tf,1),OBJPROP_COLOR,MediumSeaGreen); ObjectSet("ArrowUP"+iTime(sy,tf,1),OBJPROP_WIDTH,1); ObjectSet("ArrowUP"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1)); ObjectSet("ArrowUP"+iTime(sy,tf,1),OBJPROP_PRICE1,iLow(sy,tf,1)); } } // --------------- Check for a downward crossover of the local downtrend ----------------- if (add) { if (NormalizeDouble(TempIND[1],8) < NormalizeDouble(yUP,8) && NormalizeDouble(TempIND[2],8) >= NormalizeDouble(yUP2,8)) { CrossDN = true; CrossUP = false; if (Arrow) // If the use of signal indicators is allowed { if (ObjectFind("ArrowDN"+iTime(sy,tf,1))<0) ObjectCreate("ArrowDN"+iTime(sy,tf,1),OBJ_ARROW, 0 ,iTime(sy,tf,1), iHigh(sy,tf,1)+15*pt); ObjectSet("ArrowDN"+iTime(sy,tf,1),OBJPROP_ARROWCODE,242); ObjectSet("ArrowDN"+iTime(sy,tf,1),OBJPROP_COLOR,Chocolate); ObjectSet("ArrowDN"+iTime(sy,tf,1),OBJPROP_WIDTH,0); ObjectSet("ArrowDN"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1)); ObjectSet("ArrowDN"+iTime(sy,tf,1),OBJPROP_PRICE1,iHigh(sy,tf,1)+15*pt); } } } } // ----------------------------------------------------------------------------------------------------------- // The conditions here can be represented by CrossGUP and CrossGDN, as well as CrossUP and CrossDN. // In the first case, we have a "global trend" on all specified bars, // in the second case, we have local trends on the last two extrema. // You can also combine signals of crossovers of "global" and "local" trends in the A/D chart /* if (CrossGUP && CrossUP) return(11); // An upward crossover of both downtrends else if (CrossGDN && CrossDN) return(-11); // A downward crossover of both uptrends else if (CrossGUP) return(10); // An upward crossover of the "global" downtrend else if (CrossGDN) return(-10); // A downward crossover of the "global" uptrend else if (CrossUP) return(1); // An upward crossover of the "local" downtrend else if (CrossDN) return(-1); // A downward crossover of the "local" uptrend */ if (CrossGUP || CrossUP) return(1); else if (CrossGDN || CrossDN) return(-1); else return(0); }
上記のコードは、すでに分析しているものとほぼ同じです;それ故、その関数の入力パラメーターとその呼び出しメソッドのみコメントします。
int SignalCrossIND(double &TempIND[], int nBars, string sy, int tf, bool local=false, bool add=false, bool Arrow=true)
以前見たコードとは対照的に、ターミナルのメインウィンドウのシグナルインジケーターを表示するbool type - Arrowという別の変数をここで追加しました。その値は標準として、trueに設定され、つまり、その矢印がそのチャートに表示されれば、このパラメーターはその関数の呼び出し時に除外されます。
一般的な場合、その関数の呼び出しは以下の通りです。EAのpグローバル変数に以下の行を挿入してください。
double TempIND[]; // Array for storing indicator data
それから、トレーディングシグナルの受け取りのための関数にて、A/Dインジケーターチャートの極値の特定のための履歴バーの望ましい数を特定します。
int nBrs=30;
もしくは、例えば
int nBrs=200;
すべてはTF トレンド線が描画されるところや、履歴のバーがいくつ処理されるかに依存します。その値が大きければ大きいほど、そのトレンド線は、A/Dチャートにてより安定し、その交差シグナルはより遅延します。
そして、最終的な関数の呼び出し:
int sig=0; sig=SignalCrossIND(TempIND, nBrs, NULL, 5, false, false, true); if (sig==1) {Code for opening a Buy position} if (sig==-1) {Code for opening a Sell position}
ここで渡されるパラメーター値は、そのデータがその現在の証券 (NULL)のチャートから取得され、使用されるタイムフレームは、M5 (5)で、ローカルトレンド線は描画されず、これらの線の交差は探されることはなく (false)、そのトレンドの方向での交差も探されません (false)が、一方、そのシグナルインジケーターはメインチャートウィンドウにて表示されます(true).
これらすべては標準の値ですので、以下の関数は完全に同一になります:
sig=SignalCrossIND(TempIND, nBrs, NULL, 5);
まとめ
上記をまとめると、この関数は決して独立したトレーディング戦略ではないと言えます。それは単にA/Dインジケーターチャートやインジケーター線に描画されたトレンド線の交差の特定における関数でしかありません。またその他の関数と同様EAにても使用されます。
その機能性を確認するために、01.01.2009から01.01.2010のインターバルでのテストを実行しました。そのテストはこの関数によって返されるその値のみを用いて実行されました。EAのシグナルモジュールを停止し、この関数と取り替えます。極値の特定におけるバーの数は、250です。そのタイムフレームは5で、選択されたそのシンボルはEURUSDで、最初のデポジッットは10000で、固定ロットは 0.1でした。
またSL とTPの使用を停止しました。設定された利益レベルに達すると、3回以上ポジションを閉じるトレーリングストップのみを残しました。ポジションは、その関数のシグナルを取得したのちオープンされました。
その反対のポジションは、この場合、閉じられていません。すでにあるポジションの方向の新しいシグナルの受け取り後、以前のポジションがオープンした後の経過時間を決定し、もしそれが7分以上であれば、新しいポジションがオープンされます。そのデポジットを最大限に使用しました。ほぼ言い忘れていたこととしては、すべてのポジションを一度に閉じるために、設定されたパーセンテージ分の資産の増加を用いました。このテストでは5%に設定しました。
1年間分のデータを示す結果のチャートは、以下の通りです:
実際のアカウントにはまだ適用できませんが、重要な部分ではあると思います。
そして、深い感謝の意をViktor (Vinin) とAleksey (Mathemat)に心優しい助けやサポートに関して示し、また異なるプログラミングの問題を解決に貢献してくれた方々に感謝しています。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/1357




- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索