VTのGannSwingOscillatorをMT4に移植する術。

hpで、VTのインジケーターをMT4に移植されているのを拝見しました。
Fibonacci bollinger bands ,VTを見て、ぶしつけだと思いますが、ご教授いただけないかと思いメールを出させていただきました。
現在、GannSwingOscillator をMT4に移植しようと思ったのですが、MQL4とはかなり勝手が違い、FXAのドキュメントを読んでも、インジケータビルダーのFormula では構文意図がよくわからず、中断しております。
いくつか(ほとんどメイン部分の要だとは思いますが)、教えていただけないでしょうか。

唐突にそんなことを言われても困ってしまうのですが...><;


でも、この人の気持ちはよくわかります。VT/CT ( VisualTrader/ChartTrader ) に使われている 'CT simple language' という言語は、MQL4ユーザが見るとほとんど意味不明に感じますからね^^;
私も真面目に CTSL を勉強した訳ではないので詳しくないのですが、相場に生きる人間はそんな甘いことも言っていられないので、現場の独断で強硬に解説を進めてみます。


CTSL からの移植 ( ある種のリバースエンジニアリング?? ) に興味のある人のみ、続きをどうぞ。orz






そもそものアプローチの仕方として、
(1) ググって見つかる先人の知恵を利用する。
(2) GannSwingOscillator の文献を調べて、定義を理解した上で、それを MQL4 で実装する。
が考えられて、どちらも正攻法なのですが、万が一、VTの GannSwingOscillator が特殊な計算式で作られていた場合、VTと同じものが出来上がる保証が無いので、移植したことになりません。厳密に移植する為には、VT のコードを調べてゆく必要があります。


Us:= BarsSince(Sum(H > Ref(H,-1),2)=2);
Ds:= BarsSince(Sum(L < Ref(L,-1),2)=2);
Hc:= HighestSince(1,Us=0,H);
Lc:= LowestSince(1,Ds=0,L);
Sd1:= If(Us=0, 
             If*1;
Sd2:= If(Sd1=1,
              If(Ref(BarsSince(Sd1=1),-1) > Ref(BarsSince(Sd1=-1),-1), 1, 0), 
              If(Sd1=-1, 
                       If(Ref(BarsSince(Sd1=1),-1) <  Ref(BarsSince(Sd1=-1),-1), -1, 0),
                        0));
TD1:= ValueWhen(1,Sd2<>0,Sd2);

VTのGannSwingOscillatorのコードは上記のような短いモノ( 改行無しでたった7行)なのですが、MQL4ユーザにはどうしてこれでインジケータが出来るのか?不思議に感じると思います。
for ループは? SetIndexBuffer は? extern は?・・・みたいに疑問が湧くのです。


MQL4 ユーザが CTSL を理解する時は、大雑把に以下のようなコードを想像すると良いです。

for(int i=Bars-1;i>=0;i--){
  Us[i] = BarsSince(Sum(High[i] > High[i+1],2)==2);
  Ds[i] = BarsSince(Sum(Low[i] < Low[i+1],2)==2);
  Hc[i] = HighestSince(1,Us[k]==0,High);
  ////中略
  TD1[i] = ValueWhen(1,Sd2[k]!=0,Sd2);
}

左辺に並ぶ変数( Us , Ds , Hc , Lc..., TD1 )は、全て配列で、forループの中の処理だけを抜き出して書いてあるイメージです。
演算子の書き方も多少異なります。これは、Pascal系の言語に特有な書き方です。

演算子 MQL4 CTSL
代入 = :=
等号比較 == =
不等号 != <>

extern (=外部入力) に相当する部分は、GUIで設定します。

↑このインジケータには、外部指定するパラメータは無いことが分かります。




SetIndexBuffer (=出力) も GUI で設定します。

↑この画面で、TD1 が唯一の出力だと分かります。





外部入力 , forループ , 出力 の仕組みが理解できれば、1つの壁を超えたことになりますが、依然としてコードの意味は不明だと思います。そこで、とるべき手段は、複雑なコードを徹底分解して、少しずつ、その意味を推理してゆく作業です。例えば、Us というバッファ配列がどんなラインを描くか知りたかったら、以下のようにします。

Us:= BarsSince(Sum(H > Ref(H,-1),2)=2);
TD1:= Us;

直接、出力配列 TD1 に代入して、

TD1:= BarsSince(Sum(H > Ref(H,-1),2)=2);

と書き直しても良いです。下図が Us 配列 のデータを示すチャートです。


このインジケータの形を見て式の意味が推測できれば良いですが、分からないので関数ヘルプを調べます。

BarsSince(DATAARRAY)
Calculates the number of bars (time periods) that have passed since DATA ARRAY was true.

と書いて有りますから、BarsSince の引数も配列だと分かります。配列ならインジケータにできるはずなので、その部分だけを取り出します。

TD1:= Sum(H > Ref(H,-1),2)=2;

↑なんだかよく分からないので、無理やり分解して、比較式の左辺( 赤字部分 )だけにします。


TD1:= Sum(H > Ref(H,-1),2);

Sumのヘルプを見ると

Sum(DATA ARRAY,PERIODS)
Calculates a cumulative sum of the DATA ARRAY for the specified number of lookback PERIODs (including today).

Sumの第一引数がやはりDATA ARRAYですから、これをインジケータにすると、

TD1:= H > Ref(H,-1);

ここまで分解したインジケータをチャートに描いてみれば、容易に推理出来ると思います。
これは、高値が更新されたら真(1)、そうでなければ偽(0)となる高値更新インジケータです。
ちなみに、Ref(H,-1);は、Highの1本前のデータを示します。


MQL4なら

for(int i=Bars-1;i>=0;i--){
  if(High[i]>High[i+1]){ // 一本前は MQL4 では +1 , CTSL では -1
    TD1[i]=1;
  }else{
    TD1[i]=0;
  } 
}

です。
分解して意味がつかめたら、逆にたどって推理してゆきます。

TD1:= Sum(H > Ref(H,-1),2);

↑これは、高値更新インジケータの連続する2つのバッファを足したもの。得られる値の範囲は0,1,2になります。

TD1:= Sum(H > Ref(H,-1),2)=2;

↑これは、「高値更新インジケータの連続する2つの値を足したもの」が、2のときは真(1)、そうでなければ偽(0)。
つまり、、、高値更新が2連続したら真(1)、そうでなければ偽(0)です。

TD1:= BarsSince(Sum(H > Ref(H,-1),2)=2);

↑これは高値更新が2回続いた Bar から、何本 Bar が経過したのか?になります。
この時点で、もう一度、上の Us 配列をインジケータにした画像をみると納得出来るのではと思います。





これで最初の2行は、

Us:= BarsSince(Sum(H > Ref(H,-1),2)=2);
Ds:= BarsSince(Sum(L < Ref(L,-1),2)=2);

Us は、高値更新が2回続いたBarから、何本Barが経過したのか。
Ds は、安値更新が2回続いたBarから、何本Barが経過したのか。
と推理できましたので、次にゆきます。

Hc:= HighestSince(1,Us=0,H);
Lc:= LowestSince(1,Ds=0,L);

HighestSinceのヘルプを見ると

HighestSince(Nth,EXPRESSION,DATAARRAY)
Returns the highest value of DATA ARRAY since the Nth most recent occurrence of EXPRESSION was true.

で、Hc は、Us==0( 高値更新が2回続いたBar )から、(計算中の)現在までの期間での高値(H)の中の最高値(Highest)を表す..
と..わかる..かな?^^;
分かったとして、次に行きます。




おそらく最大の難関となる式がこれ。

Sd1:= If(Us=0, 
             If( (L<>Lc) AND (Ref(L,-1)<>Lc), 1, 0), 
             If(Ds=0, 
                    If( (H<>Hc) AND (Ref(H,-1)<>Hc), -1, 0),
                     0));

↑判りやすいようにインデントしてありますが、1行で書かれていたら意味不明でしょう..。
この式には、以下のような If 構文が4組3段階の入れ子で組まれています。条件式が青字の部分です。

CTSL表記
S:= If( A=0 , B , C);
S:= If( A<>0 , B , C);


MQL4的表記
if( A == 0 ){ S = B; }else{ S = C; }
if( A != 0 ){ S = B; }else{ S = C; }


MQL4で書くと以下のような構文です。

if(Us[i] == 0){
	if( Low[i] != Lc[i] && Low[i+1] != Lc[i]) {
		Sd1[i]=1;
	}else{
		Sd1[i]=0;
	}
}else{
	if(Ds[i] == 0){
		if( High[i] != Hc[i] && High[i+1] != Hc[i]) {
			Sd1[i]=-1;
		}else{
			Sd1[i]=0;
		}
	}else{
		Sd1[i]=0;
	}
}


これでGannSwingOscillatorに使われる残りの構文については、時間さえ掛ければ理解できるでしょう..。
Sd1から、Sd2、そしてTD1 への流れは、それぞれのバッファをインジケータにしてグラフを見る方が楽に推理できます。

Sd2は、Sd1が -1 or 1 のシグナルを示すと見なして、それが反転した瞬間に -1 or 1 を示し、それ以外の時はゼロになるパルス関数のようになっています。Sd1で -1 が連続しても Sd2 では、最初の1度しか反応しません。
TD1は、Sd2のパルスの示した値が反転するまで -1 or 1 の値をそのまま保持しています。

(グラフの形から式の意味を推理できたら、もう一度ヘルプを頼りに、式の内容を確認してくださいねっ。念のため…^^;
それから、このようなバッファ配列を分解して調べてゆく方法は、他人の難解な MQL4 を読み解く場合にも役に立ちます...。


MQL4 では、Sd2 をわざわざ計算しなくても TD1 を求めることができるので、その部分は省略して、それ以外をなるべく忠実に移植したサンプルをこちらに置いています。CTSL から MQL4 へ移植に苦労している人のお役に立てれば幸いです。

*1:L<>Lc) AND (Ref(L,-1)<>Lc), 1, 0), If(Ds=0, If((H<>HC) AND (Ref(H,-1)<>Hc), -1, 0), 0