ブログトップ 記事一覧 ログイン 無料ブログ開設

とあるMetaTraderの備忘秘録 RSSフィード

忙しいです。ネタもないし・・・

2010-01-21

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((L<>Lc) AND (Ref(L,-1)<>Lc), 1, 0), 
             If(Ds=0, 
                    If((H<>HC) AND (Ref(H,-1)<>Hc), -1, 0),
                     0));
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系の言語に特有な書き方です。

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


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

f:id:fai_fx:20100120114843p:image

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




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

f:id:fai_fx:20100120114859p:image

↑この画面で、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 配列 のデータを示すチャートです。

f:id:fai_fx:20100119173659p:image


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

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 への流れは、それぞれのバッファをインジケータにしてグラフを見る方が楽に推理できます。

f:id:fai_fx:20100119165338p:image

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

TD1は、Sd2のパルスの示した値が反転するまで -1 or 1 の値をそのまま保持しています。

(グラフの形から式の意味を推理できたら、もう一度ヘルプを頼りに、式の内容を確認してくださいねっ。念のため…^^;

それから、このようなバッファ配列を分解して調べてゆく方法は、他人の難解な MQL4 を読み解く場合にも役に立ちます...。


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

2009-12-11

fibonacci bollinger bands にアラートを追加してみた。

violin さんからの依頼で、fibonacci bollinger bands にアラートを追加してみました。ダウンロードは、こちらからどうぞ。

(BBFiboって、大した計算をしている訳ではないのに、なぜか、タッチして反発しているように見えるのが怖いです..。

チャートに5本の線を描けば、必ずどれかは当たる(!)と聞きますので、気のせいだと思うようにしてますが..。

f:id:fai_fx:20091210151111g:image

バンドに価格が到達すると、アラートとメールを送ります。

f:id:fai_fx:20091210152010p:image

一応要望通りに、アラート(メール)の時間間隔を分単位で指定できるようにしましたが、どうでしょうか…。

2009-11-03

評価残高が一定額を超えたら、ポジションを閉じてEAを止める方法。

FX口座の評価残高が10%増えたらEAを止めたい。あるいは、25%損失が生じたら止めたい…といった場合に役に立つ(...かもしれない...) script が、EquityProtecter(sc).mq4 です。EAを止めると同時にポジションをクローズし、指値注文を削除するので、EAを使わない裁量トレードでもポジションを放置したい時に使えます。



海外のフォーラムには、より多機能な EquityGuard というEAがあるのですが、EA自体がEAを止めるという枠組みは上手くいかない気がするので、script で簡易なものを作り直しました。

使い方は、scripts フォルダにコピーして、チャートに貼り付けるだけです。


extern bool UseTargetEquity = true;
extern int TargetEquityPercent = 110;
extern int TargetEquity = 0;

UseTargetEquity を true にすると、目標残高を設定できます。

TargetEquityPercent = 110 であれば、評価残高が10%増えた(=110%を超えた) 時点で止まります。

%の計算は、script を貼り付けた時点での評価残高が基準になるので、絶対値で評価残高を指定したい場合は、TargetEquity にゼロ以外の値を設定します。


extern bool UseProtectEquity = true;
extern int ProtectEquityPercent = 50;
extern int ProtectEquity = 0;

同じ要領で、評価残高の下限を設定できます。この設定値を下回った時点で止まります。

(ただし、設定値を下回った時点からポジを成行きで閉じるので、スリッページ、遅延等により口座残高が予想以上に減ってしまう可能性はあります。)

extern bool UseStartTime = false;
extern string StartLocalTime ="09:00";

script による評価残高チェックを指定時刻まで行いたくない場合は、UseStartTime を true にして、StartLocalTime にローカル時間(パソコンの時刻)での時刻を指定します。

(この機能を使うべきかどうかは、EAの性質をよく把握してから判断した方が良いです...。

2009-09-29

MetaKeyXのECN対応とMTF_MAのCrossAlert

nuwitter さんが、MetaKeyXのECN対応をしてくださったので、(無断で) ここに公開しておきます。

(私の製作物の改造版の作成、再配布は自由に行ってくださって構いません..)

http://u3.getuploader.com/mt/download/158/MetaKeyX_v1.2_ecn_20090928.mq4

ODLのような注文種別がカウントダウン(Market Execution) となるブローカーでは、注文時にストップが入れられないので、このバージョンをお使いくださいませ。



MTF_MAのCrossAlert対応

f:id:fai_fx:20090929001352g:image

ポポさんから依頼のあったMTF_MAのCrossAlert版を下記に置きました。

MTFのMAが交差した瞬間、もしくは、交差が確定した時点のどちらかでAlert を表示します。

http://u3.getuploader.com/mt/download/160/%23MTF_MACross.mq4

特に工夫も何も無いので、煮るなり焼くなり好きに改造してもらえればと思います。

2009-08-17

Bidラインの色だけを変える。

f:id:fai_fx:20090816205354g:image

MT4 では、Bidラインだけの色を変えることは出来ません。F8キーを押して、プロパティでグリッドの色を変えれば、Bidラインの色も一緒に変えられるのですが、

グリッドはグレーのままにしておきたい人にはちょっと不便ですよね。

そんな時に役立つのが、Bid_Line.mq4 で、コードとそのポイントは以下の通りです。

#property indicator_chart_window

extern color BidLineColor = DodgerBlue;
extern string BidLineName = "Bid_Line";
int init()
{
   SetIndexLabel(0,NULL);
   return(0);
}
int deinit()
{
   ObjectDelete(BidLineName);
   return(0);
}
int start()
{
   if(ObjectFind(BidLineName) == -1){
      ObjectCreate(BidLineName,OBJ_HLINE,0,0,Bid);
      ObjectSet(BidLineName,OBJPROP_COLOR,BidLineColor);
   }
   ObjectSet(BidLineName,OBJPROP_PRICE1,Bid);
   return(0);
}

単純なコードですが、気になるポイント?を説明しておきます..。

   SetIndexLabel(0,NULL);

これは、データウィンドウに余分な表示を出させないための一行です。

上のインジケータは、indexbuffer を使っていないのにも関わらず、この一行が必要となります。



次のポイントは、ObjectCreate() のタイミングを start() 内で行っている点です。

考え方として

(1)init() 内で、ObjectCreate()

(2)start() 内で、ObjectSet()

(3)deinit() 内で、ObjectDelete()

という処理をやりたくなるのは、自然な流れなのですが、これには欠点が2つあります。

欠点1. ユーザが、うっかりオブジェクトを選択削除してしまった場合のリカバリが自動で出来ない...

欠点2. init()内では、サブウィンドウにオブジェクトを作れない

で、その欠点を回避できるのが、

(1)start() 内で、ObjectCreate(),ObjectSet()

(2)deinit() 内で、ObjectDelete()

なのです。詳細な説明は省略しますが、コメントに疑問等を書いてもらえれば後日 日記にしまする...。

(今夜は歩き疲れてフラフラなのです。。。><