Forex Tester 2 のインディケーターで HL バンドを使いたい

最近、ようやく “検証する” というのがどういうことかが理解できてきて、実践することができています。 Forex Tester 2(FT2) で検証するために、HL バンドを使いたかったのですが、 “HL バンド” という名前のインディケーターがなかったので、調べてみました。

HL バンド?

HL バンドとは、チャート上に直近 n 日の高値(H バンド)、安値(L バンド)の 2 本のラインを引いてレジスタンスやサポートを確認したり、高値、安値のブレイクを判断するテクニカル指標です。

HLバンド | チャート | 情報 | MARKET SPEED FX オンラインヘルプ | 楽天 FX | 楽天証券

FT2 の HL バンド

“HL バンド” という名前のインディケーターは FT2 にないみたいでしたので、MT5 のインディケーターと同様に自分で作成してみようと思ったのです。

が、HL バンドってあまり海外ではメジャーな名前ではないのでしょう。 調べていると、Donchian Channel という名前の方が知られているようでした。 Donchian というのは開発者の名前ですね。 “HL バンド” を Google で検索すると、あまり信用できないような個人のブログが上位に出てきますし・・

もっと調べてみると、 “Price channel” という名前で同じインディケーターがあるようでした。 ので、自分で作成する手間がなくなりました。

MT5 にも同様な “Price_Channel” という名前のインディケーターがありましたね。 こちらは標準ではなく、カスタムの扱いになっているようです。

こちらは FT2 の Price channel です。

こちらは MT5 の Price_Channel です。

MT5 の Price_Channel は中間線があって、そこは良いと思うのですが、高値と安値の間が塗りつぶしになってしまうところが見づらいですね。

これに関して、パラメーターだけでは変更できないので、ソースを調べてみました。

#property indicator_type1 DRAW_FILLING の指定がしてあって、これはドキュメントから引用すると次の意味があるようです。

DRAW_FILLING

DRAW_FILLING スタイルは、2 個の指標バッファの値の間の色の領域をプロットします。 実際に、このスタイルは 2 つの線を描画しそれらの間の空間を指定された 2 つの色のいずれかで塗りつぶします。 これは、チャネルを描く指標の作成に使用されます。 いずれのバッファも空の値のみを含むことはできません。 この場合プロットがなされません。

塗りつぶしの色は 2 つ設定することが出来ます。

  • 最初の色は、1 番目のバッファの値が 2 番目の指標バッファの値よりも大きい領域に使用されます。
  • 2 番目の色は、2 番目のバッファの値が 1 番目の指標バッファの値よりも大きい領域に使用されます。

カスタム指標 / 指標スタイルの例 / DRAW_FILLING - MetaTrader 5 のためのアルゴリズムの/自動化されたトレーディング言語のリファレンス

Price_Channel という名前からもわかるように、チャネルですから、このように描画していて当然な感じもします。 そうすると、FT2 の方はラインで描画されていて、逆に違和感もあるような・・ Price_Channel という名前にしてしまったのがよくないのでしょうか・・ でも、見やすさを優先して、MT5 の Price_Channel もラインで描画されるように修正してみました。

それから、描画する位置が 1 つ分シフトしてあったようなので、シフトしないようにしました。

修正した FT5 の Price_Channel です。

ソース

高値と安値を計算するのに、関数を実装していたのですね。 配列関数の ArrayMaximumArrayMinimum でも良いのではないかと思いつつ、そのままにしています。

//+------------------------------------------------------------------+
//|                                               Price_Channell.mq5 |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2009, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
//--- indicator settings
#property indicator_chart_window
#property indicator_buffers 3
#property indicator_plots   3
#property indicator_type1   DRAW_LINE
#property indicator_type2   DRAW_LINE
#property indicator_type3   DRAW_LINE
#property indicator_color1  Blue
#property indicator_color2  Blue
#property indicator_color3  Blue
#property indicator_label1  "Channel upper"
#property indicator_label2  "Channel lower"
#property indicator_label3  "Channel median"
//--- input parameters
input int InpChannelPeriod=22; // Period
//--- indicator buffers
double    ExtHighBuffer[];
double    ExtLowBuffer[];
double    ExtMiddBuffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
void OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,ExtHighBuffer,INDICATOR_DATA);
   SetIndexBuffer(1,ExtLowBuffer,INDICATOR_DATA);
   SetIndexBuffer(2,ExtMiddBuffer,INDICATOR_DATA);
//--- set accuracy
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
//--- set first bar from what index will be drawn
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,InpChannelPeriod);
//---- line shifts when drawing
//   PlotIndexSetInteger(0,PLOT_SHIFT,1);
//   PlotIndexSetInteger(1,PLOT_SHIFT,1);
//   PlotIndexSetInteger(2,PLOT_SHIFT,1);
//--- name for DataWindow and indicator label
   IndicatorSetString(INDICATOR_SHORTNAME,"Price Channel("+string(InpChannelPeriod)+")");
   PlotIndexSetString(0,PLOT_LABEL,"Channel("+string(InpChannelPeriod)+") upper");
   PlotIndexSetString(1,PLOT_LABEL,"Channel("+string(InpChannelPeriod)+") lower");
   PlotIndexSetString(2,PLOT_LABEL,"Median("+string(InpChannelPeriod)+")");
//--- set drawing line empty value
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0.0);
   PlotIndexSetDouble(2,PLOT_EMPTY_VALUE,0.0);
//--- initialization done
  }
//+------------------------------------------------------------------+
//| get highest value for range                                      |
//+------------------------------------------------------------------+
double Highest(const double &array[],int range,int fromIndex)
  {
   double res;
   int i;
//---
   res=array[fromIndex];
   for(i=fromIndex;i>fromIndex-range && i>=0;i--)
     {
      if(res<array[i]) res=array[i];
     }
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| get lowest value for range                                       |
//+------------------------------------------------------------------+
double Lowest(const double &array[],int range,int fromIndex)
  {
   double res;
   int i;
//---
   res=array[fromIndex];
   for(i=fromIndex;i>fromIndex-range && i>=0;i--)
     {
      if(res>array[i]) res=array[i];
     }
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Price Channell                                                   |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   int i,limit;
//--- check for rates
   if(rates_total<InpChannelPeriod)
      return(0);
//--- preliminary calculations
   if(prev_calculated==0)
      limit=InpChannelPeriod;
   else limit=prev_calculated-1;
//--- the main loop of calculations
   for(i=limit;i<rates_total && !IsStopped();i++)
     {
      ExtHighBuffer[i]=Highest(high,InpChannelPeriod,i);
      ExtLowBuffer[i]=Lowest(low,InpChannelPeriod,i);
      ExtMiddBuffer[i]=(ExtHighBuffer[i]+ExtLowBuffer[i])/2.0;
     }
//--- OnCalculate done. Return new prev_calculated.
   return(rates_total);
  }
//+------------------------------------------------------------------+

終わり

FT2 のインディケーターの作成についても少し調べてみたのですが、MT5 と比較して面倒ですね。 まず、開発環境が一つの心理障壁になりますね。

C++ か、Delphi で開発ができるみたいなんですけど、ソースのサンプルが Delphi の方にしかなくて、C++ のサンプルのソースは一目均衡表の一つしかありませんでした。

Delphi って・・ 2016年現在のメインストリームの開発言語じゃないですよね・・ 学生時代に授業で Delphi を使ってアプリケーションを作ったことがありますけど、GUI のクライアントアプリケーションをお手軽に作るなら Visual Studio でも良いかなって思ってしまいます。

これに関して調べてみたら、Yahoo!知恵袋にこんな書き込みがありました。

Delphiがメジャーでない理由を考えるに、当時の対VisualBasicという側面で、 “Pascal言語であること” がDelphiのシェア拡大の足を引っ張ったという要素は否めないのですが、実はあまり言語仕様とか性能といったことは問題ではなくて現代の開発者のハートを掴めるキラーアプリ不在が問題なのではないですか。

要するにその言語・環境を使えば「自分は何ができる様になるのか」が問題なのであって例えば最近はPHP,Rubyのようなスクリプト言語が人気ですがそれは “Webアプリが簡単に作れる” といった分かりやすいネタがあったり周りに使っている人が多い(=情報も集めやすい)から、とりあえずそこから入門している訳で、 言語仕様や性能に惚れ込んで入門するような人はむしろ少ないのでは。

Delphiは、一昔前の2層C/SなDBアプリを作るのが当たり前だった時代ならまさにそのあたりが得意分野ですからVisualBasicやPowerBuilder、あるいはSQLWindows(Centura)といった当時の競合達と比べても大枚はたいて買う価値があり、実際その頃はシェアとしても悪くなかったようですがその後、流行はインターネット(web)ベースのシステム開発に移り、Delphiのそちらへの適応はイマイチで、競合ははるか先に行ってしまいました。

そう思うと、RubyからみたRuby on Railsのようなキラーアプリの存在は大きいと思います。 対して、Delphiは有償ツールなのに、そんなフリーの言語で簡単にできることができないのならそりゃ開発者も寄ってこないのではないですか。

そんなこんなで、流行を掴み損ねて開発者人口が減る
→マイナーなので仕事で採用しにくくなる
→さらに開発者人口が減るという悪循環にはまってしまい、とりわけ日本ではそれが顕著に表れていると思います。

Delphiはなぜ廃れた?超メジャー言語からは、一歩後ろの… - Yahoo!知恵袋

この書き込みはすごくわかるなー、と思うところがあります。

Power Builder とか懐かしいなー、と思いますし。 今も使っている現場はあるのでしょうけど。 そういえば、2012 年に関わっていたプロジェクトでは、まだ Power Builder を使っていたみたいで、経験したことのある自分が加わったこともありました。

見せ方って大切ですよね。 あまり良くないものでも良さそうに見せたら多くの人が集まりますし。 良いものでも見せ方が悪かったら人は集まらないですし。でも、真実とは程遠い。

FT2 のこんな一面も見て、MT5 はそれ自体に開発環境が含まれていて、心理障壁が一つ少なかったのだと実感することができました。

FT3 がもう販売されているのですが、仮想マシンではまだ動かすことができないみたいです・・ 自分は仮想マシンで MT5 も、FT2 も使っているのです・・