MT4ネタです。
MT4において、プログラミングにより別フォームでグラフを表示できないかと模索したので、示します。
1 はじめに
MT4では、dllを使えば、C言語のサブルーチンを追加できます。
そこで、Cを使った場合、バックテストのグラフを一瞬で表示できないかというのが、ここでの話題です。
私は、MT4のエキスパートアドバイザーを使うことなく、BuyEnter[],BuyExit[],SellEnter[],SellExit[]という、参入、撤退時の終値の配列を使い、
インディケータの立ち上げ時に、インデックスをBarsから1まで、ストラテジーを回して、ほぼ一瞬で、バックテストを行い、
最大ロス、最大ロスが生じた時刻、トータルゲインなどを計算するようにしています。
これにより、エキスパートアドバイザーとは比較にならない、格段に速いストラテジーの繰り返しができるようになりました。
しかしながら、損失のボラリティも見ておきたいと思うようになり、グラフ表示を簡易にできないかと模索しました。
VISUAL Cを使っているので、そのフレームで使いやすいものを探しました。
2 Cでグラフを表示する方法論
ネットで調べると、以下の2つの方法が見つかりました。
(1) matplotlib-cppを使う方法 (素人にはむつかしい)
(「C++でグラフ描画をするならmatplotlib-cppを使ってみる?」)参照
この方法は、PythonのコマンドをCでたたくという方法のようで、matlabのような使い心地のようです。
matlabについて、私は大学で制御理論をやっていたので、私もシミュレーションで使っていました。
ところが、このmatplotlib-cppは、もともと、unix系の言語で書かれており、ラズベリーパイでやったgitなどバージョン管理しつつ、適切なダウンロードができるものと異なり、
VISUAL Cでやるには、matplotlib-cppをダウンロードしたら簡単に使えるものではありませんでした。
matplotlib-cppが利用しているPythonをCで使うためのライブラリーその他のバージョン間のエラーを訂正しなければならず、
VISUAL Cで使う方法も、ネットで紹介されていますが、それだけを追試しただけでは動きませんでした。
この手のエラーは、ネットでエラーコードを検索しつつ、エラーを訂正していくのですが、私には手に負えず諦めました。
なお、この方法では、PythonをCから利用するうえで、そのライブラリがーが入っているPythonのランニング環境をウインドウズにインストールする必要があり、それをStoreからインストールしてはダメ、また、Programフォルダにインストールすると、管理権限などで、実行できないなどという泥沼に陥ります。Pythonのインストールのオプションについてかなり注意を払う必要もあり、3回ぐらいインストールしなおしました。
また、インタプリタ言語を、Cからたたいてわざわざ使うのか、というのも疑問です。
(2)GNUPlotを使う方法 簡単
i)gnuplotのインストール
GNUPlotは、unix系のコマンドラインのようですが、ウインドウズにインストールできます。
GNUPlotをインストールして、GNUPlot エンターと打ち込むと、独自のコマンドラインが開始されます。
Cからこのプログラムを操作するには、そのコマンドに、オプションなどを、fprintを介して、送り込むだけなので、Pythonやバージョン違いなどの面倒はありません。
まずは、外部サイトの「[Windows] C言語でgnuplotを動かす」を参考に、
サイン関数のグラフを書いていくことを、目標にします。できたときは感動しました。
MT4につき、ほかのサイトで見たこともないのですし、matplotlib-cppで散々苦労しつぶれた後だったので。。
dllや、C言語の基本的な使い方は、ほかのサイトをご参照ください。
ii)インストール後
インストールしたら、visual C上で、GNUPLOTをインストールしたパスを、「GNUPLOT_PATH」として定義します。
以下は、上記サイトのコードを一部引用しています(以下の「引用開始」~「引用終了」までの範囲,なお、exitをreturnへ変更適用)。
const double* BuyEnter,
const double* BuyExit,
const int bars00, const int all_bars,
const double* SellEnter,
const double* SellExit,
const time_t* time0,const double spread)
{// 引用開始
FILE *gp; // For gnuplot// gnuplotの起動コマンド
if ((gp = _popen(GNUPLOT_PATH, “w”)) == NULL) { // gnuplotをパイプで起動
fprintf(stderr, “ファイルが見つかりません %s.”, GNUPLOT_PATH);
return(EXIT_FAILURE);
}// — gnuplotにコマンドを送る — //
fprintf(gp, “set xrange [-10:10]\n”); // 範囲の指定(省略可)
fprintf(gp, “set yrange [-1:1]\n”);fprintf(gp, “plot sin(x)\n”); //sin(x)を描く
fflush(gp); // バッファに格納されているデータを吐き出す(必須)
system(“pause”);
fprintf(gp, “exit\n”); // gnuplotの終了
_pclose(gp); //引用終了
return(0);
}
_pcloseなどの終了処理も、忘れずに行ってください。
MT4側のプログラムは、最初のほうの宣言部で
int Profit_Plot_dll(
const double& BuyEnter[],
const double& BuyExit[],
const int bars00,const int all_bars,
const double& SellEnter[],
const double& SellExit[],
const datetime & time0[],const double spread);
#import
プログラム部分で
BuyEnter,
BuyExit,
barsL,Bars,
SellEnter,
SellExit,
TimeL,Setted_Spread);
とします。変数はあらかじめ適宜宣言しておいてください。barsLについては、「const int barsL=Bars;」と、init内で定義します(後述)。
ここで、配列につき、[]、&、*が登場しますが、詳しく理解できなくても、そういう決まりだと思って、テンプレートをコピペでもよいと思います。
サブルーチン内で値を変化しないなら、「const」をつけておくと、エラー検出につながります。
また、引用する変数の型のint,double,time_tの変数たちなどを一か所にまとめないで、織り交ぜておくと、変数の順序の記述が間違っていた場合に、エラーが出やすくなり、間違いの検出につながります。
それゆえ、あえて「const int bars00」をdoubleの間に挟んでいます。
まずは、これで、サイン関数のグラフを描けるかを確認します。
なお、dllは、修正してコピーする繰り返しや頻度が、かなり大変なので、visual CのReleaseのフォルダから、MT4の\MQL4\Librariesへコピーするショートカットを作ります。
さらにそのショートカットのフォルダをアクティブにして、クリックするのも邪魔くさくなるので、ショートカットをタスクバーに配置して、タスクバーのワンクリックで、dllをMT4へ転送するようにします。
iii)次に、散布図を描いていきます。
その前提として、
C言語内で、Xをキャンドルの数、Yを累積利益、Zを1回限りの利益として、配列を用意します。
配列について、領域を確保しておかないと、バイオレンスエラーとなりますので、以下の通り、領域を確保します。
double* Y = NULL;
double* Z = NULL;
X = (int*)malloc(sizeof(int) * bars00);
Y = (double *)malloc(sizeof(double) * bars00);
Z = (double *)malloc(sizeof(double) * bars00);
・・・free(X);free(Y);free(Z);
この辺も、テンプレートを丸コピーでよいです。ここで、bars00は、インディケータ起動直後のバーの数です。
MT4でArrayResizeを使い、領域を確保したうえで、C言語のほうでそれを参照させたほうが、簡単、確実のように思います。
サイトをたくさん読みながら、本件に合わせましたが、C言語のサイトでは、intしか説明していないサイトが非常に多く、エラーを消すのに苦労しました。
iv)ファイルを使わずに、散布図をプロットする方法
ここで、GNUPlotにつき、たくさんのサイトでは、(x、y)の値のデータが記録されたファイルを読み込んで、プロットしていく旨を説明していることが多いです。
しかし、配列を参照してデータをメモリーに保有しているのに、ファイルに書き出したりするのは面倒なので、ファイルを使わずに散布図をプロットしているサイトを探しました。
どうやら、
などを宣言した上で、for(int j=1;j<bars00;j++)で繰り返しつつ、
各座標について
のコマンドにより、座標を書き込んでいきます。
上記の「plot」をあらかじめ宣言しないと、座標を書き込んでも、グラフは表示されません。それもわからなくて、沈み込んでしまいました。
その他座標軸の使い方も、たくさんのサイトがございますので、それをご覧ください。
v)利益計算のプログラムの例
利益計算から最後までの部分は、例えば、以下の通りになります(tabは消えてしまっていますが・・・)。
// ↓↓— gnuplotコマンド開始 —↓↓ //
double AccGain = 0;
fprintf(gp, “plot ‘-‘ u 1:2 t ‘AccGain’ w lp\n”);
for (int j = 0; j < bars00;j++)
{
if (X != NULL) { X[j] = j; }
if (BuyExit[j] != NULL && buyenter != 0)
{
Z[j] = BuyExit[j] – buyenter – spread;
AccGain = AccGain + Z[j];
Y[j] = AccGain;
}
else if (SellExit[j] != NULL && sellenter != 0)
{
Z[j] = – SellExit[j] + sellenter – spread;
AccGain = AccGain + Z[j];
Y[j] = AccGain;
}
else
{
Z[j] = 0;
if (Y[j – 1] != 0) { Y[j] = Y[j – 1]; }
else { Y[j] = 0; }
}
if (BuyEnter[j] != NULL)
{
buyenter = BuyEnter[j]; sellenter = 0;
}
if (SellEnter[j] != NULL)
{
sellenter = SellEnter[j]; buyenter = 0;
}
//if (Y != NULL) { Y[j] = j; }
if (X != NULL && Y != NULL && Y[j]>= -100 ) { fprintf(gp, “%d %f \n”, X[j], Y[j]); }
}
fprintf(gp, “set xrange [1:%d]\n”,bars00);
fprintf(gp, “e\n”);
fflush(gp); // バッファに格納されているデータを吐き出す(必須)
system(“pause”);
fprintf(gp, “exit\n”); // gnuplotの終了
_pclose(gp);
double xx = Y[9];
free(X); free(Y),free(Z);
return bars00;
}
ここで、MT4では、シリーズ配列については、0番が最新の確定していない足を表しますが、
dll上では、その渡された配列は、インデックスが変わってしまいます。
dll上では、インディケータ開始直後の一番古いものが、インデックス0として、参照され、
インデックスが「現在のバーの数Bars(キャンドルの総数)-1」の値のものが、MT4上の最新の確定していない足を表します。
現在のキャンドルの数Barsは、新しい足が記録されるごとに増えていくことがあるので、インディケータ開始直後に比べて、増えていくことがあります。
ところで、私は、MT4上で、インディケータ開始直後のバーの数を固定して、「const bars=Bars;」で定義して、オーバーフローを防いでいます(MT4上でシリーズ配列は、本来は動的配列のはずが、Resizeで定義しないとオーバーフローになることが多々あります。)。 その固定された、起動直後のバーの数が、上記のbars00に該当し、上記all_barsは、現在のバーの数Barsです。予約変数のBarsには、常々、最新のバーの数が入ります。上記でconst を付さないで定義すると、その時限りの定数を代入したつもりが、常に変動する値のアドレスが参照されるようで、思わぬ誤作動を引き起こします。
バックテストや本件のグラフ表示は起動直後に行うので、bars00だけで足ります。
以上に従えば、例えば、以下のように、表示できます。インディケータを起動した直後に、一瞬で、バックテストの利益推移を計算します。
ただし、この方法だと、このグラフのほかに、コマンドラインの黒いフォームが2つ立ち上がります。
そのうちの1つに「続行するには何かキーを押してください . . .」と表示され、何かキーを押して、この表示されたフォームを選んで消さないと、MT4が起動しません。
とりあえず、傾向を探るには、良いと思います。
なお、上記の例では、横軸がバーの数で、参入回数3500回、1万通貨で55万、5500pipsに達しています(ストラテジーは言いません)。ただし、バックテストですと、たとえ終値だけを見ていたとしても、現実とのずれは必ず生じますし、参入回数が多いほどその乖離が大きくなり、現実にはマイナス傾向の戦略になることが多々あり、苦労していました。最近は、損失を含めた総利益/回数、つまり、スプレッド余裕を重視しています。また、MT4の自動売買を提供している証券会社はわずか数社であり、サーバーに負担をかけると、強制撤退の恐れもありますので、参入回数を減らし、能率を高めていくことが重要と考えています。