やまちゃんのオーディオブログ

真空管アンプ、最近は資金調達のためのFX,MetaTrader4ネタもやっています。

mt4 fx自動売買 mql4

MetaTrader4 プロジェクト管理(やまちゃんずフレームワーク)

投稿日:2022年4月24日 更新日:

MetaTrader4(MT4)のプログラミング言語mql4のプロジェクト管理、ファイル分割について説明していきます。これらの体系的説明は今までネットを見てもほぼ皆無でした。なので、プロジェクト管理をするためのなんちゃってフレームワークを2022年4月の今、提案しようと思います。本邦初ではないかと。ここでは、mt4プログラミング言語であるmql4の高度な使いこなしテクニックも説明していきます。

オーディオを続けるには軍資金も必要ですし・・・

※MT4は、FXの自動売買を可能にするWindowsソフトウェアです。  new mt4 frame work for maneging mt4 projects

1 MT4 やまちゃん’sフレームワーク 、プロジェクト管理 はじめに

mt4のプログラムの規模が大きくなると、訂正するのに画面のスクロールが大変になってきます。なので、ファイルを分割して、該当箇所へすぐに飛べるようにします。ただ、その分割したファイルのセットも、ファイル数が増えると移動や管理が大変になってくるので、1つのプロジェクトとして管理をするのが、プログラム上、楽です。プロジェクトとして管理すれば、プロジェクト内のどのファイルをアクティブにしていても、プロジェクトに対するコンパイルは、プロジェクト全体に対するコンパイルとしてみなされ、わざわざメインルーチンのファイルに移動してコンパイルする必要もなくなります(そういう言及すら、ネット上ではほとんど見かけません)。また、この手法は、mt4のインジケータでもEAでも使えますし、後々、インジケータでもEAでも1つのプロジェクトとして管理する方法を説明していきます。

MT4プロジェクト作成のメリット

1 エラー個所を探すのに便利

ほとんど修正しない個所を切り取って別ファイルにしてしまえば、いちいちスクロールしなくて済み早く修正ができます。

2 プロジェクト内のどのファイルをアクティブにしていても、プロジェクト全体をコンパイル可能

例えば、mql4のコンパイルボタンを押すとプロジェクトに属するどのファイルを立ち上げている状態でも直ちにコンパイルできます。単なるファイル分割をしただけでは、メインルーチンのファイルに移動しないとコンパイルできませんが、プロジェクトにしてしまえば、訂正しているサブルーチンのファンクションを記述したファイルを立ち上げている状態でそのままコンパイルできるので、便利です。

3 プロジェクト単位で、1つのフォルダとして移動が可能

プロジェクトを一括して移動でき、管理上便利になります。フォルダをコピーし、フォルダ名を変えてプロジェクトの履歴を残すこともできます。

インジケータとEA(自動売買プログラム)の役割分担(まずはインジケータづくりから。。。)

mt4のEAを稼働しているときはその同一のプログラムで、動作状況をチャートに表示してインジケータのように確認することができません(ただし、コメントにより値を表示することはできます)。いきなりEAを作ったところでどのように動作しているのがわかりづらく、暗中模索でとても危険で、しかもシステムを練り上げるのに時間がかかってしまいます。

なので、まずはインジケータを作り必要な情報をチャートに表示しつつ、指標が正しく動作しているか、売買のタイミングが適切かを常に確認しながら開発を進めていく必要があり、開発のメインは、EAよりもインジケータ作りです。また、mt4では独自のインジケータを作ることができますから、例えば、特定条件に合致する場合に売買をする時期を終値のENTER[],Exit[]の配列に入れて、チャート上に表示することができますし、合計の損益計算を随時表示することも可能です。

この損益計算につき、EAアドバイザーのバックテストで損益を計算するという方法があるとおっしゃるかもしれません。巷ではこれが勧められています。しかし、ファイル分割をした場合には、Icustomなどを使うとバックテストに異常に時間がかかることがあります。そうなるとシステムを練り上げるのに時間がかかります。それを回避するため、C#でコンパイルしたり、同じファイルにそのある指標のEAのサブルーチンを乗せたりしますが、そこまでする必要がなければ不便です。また、損益の合計値という一つの指標だけで、条件に基づく売買が適正なのか判断するのも、情報が十分でなく難しいです。

なので、売買のENTER,EXITの終値をそれぞれ配列に入れて、プロジェクトの開始時に一度だけ、自作の利益計算のプログラムを回し、画面にComment()を使って表示させます。これによりパラメータを変えた場合に一瞬で利益計算ができ、パラメータの最適化にも便利です。システムを練り上げるのも相当加速します。まだできてないですが、AIによる最傾斜法などの最適化探索も可能になるかもしれません。

以上の通り、EAは、インジケータで思い通りの参入、撤退ができるようになり、その利益計算で満足いく結果が出た場合に初めて動作させるもので、開発をしていくには、まずはインジケータを作り、参入撤退時期など、必要な情報をチャートに表示しながら、確認して進めていくべきものと思います。

2 mt4プロジェクトの作り方、作成方法

では、早速始めますか。

以下では例えば、MACDのインジケータファイルからプロジェクトを作る方法について説明します。ご自身が作成しているインジケータのファイルから作ればよいです。

1 プロジェクト作成

まずは、次にファイル>「ソースから新規プロジェクトを作成」をクリックしますと、ファイルを選択できるので、メインルーチンにしているファイルを選択します。

ここでは、MACD.mq4を選択します。「.mq4」はmt4のソースファイルです。EAでもインジケータでも同じ拡張子です。

そうすると、左にナビゲータ、右にプロジェクトファイルの生成画面が出てきます。ナビゲータにファイルを登録することで、プロジェクトの一部として認識され、ファイル分割やプロジェクト管理が可能になります。右側について、「プラットフォーム」はMetaTrader 5ではなくて、MetaTrader 4に変えます。「バッファ」の数も増えてきても、ここは特に後でいじる必要はありません。

このように生成すると、indicatorのフォルダの下に、MACD.mq4と並んで、MACD.mqprojというファイルができます。

2 プロジェクトフォルダの作成

フォルダindicatorの下にフォルダを作り、そこにプロジェクト全部を格納するようにします。フォルダ名は、プロジェクト名やメインルーチンのファイル(ここでは、MACD.mq4)と関係がなくてよいです。例えば、更新の時期などを記述できます。次に、プロジェクトやメインのファイルMACD.mq4を閉じ、ファイルを開くを選択します。

なお、説明が後手に回りすいませんが、最初からフォルダを作成しインジケータのファイルを移動して、1つのプロジェクトを作ってもよかったです。ただ、いずれフォルダごと移動という作業をする日が来ますので、この手続きに習熟されるのもよいかと思います。

上記の「ファイルを開く」のファイル選択で、先ほど作ったプロジェクトを選択してください。ここでファイル開くのには、必ずプロジェクトファイル「….proj」を選択してください。そうすると、左にプロジェクトのナビゲータが表れて、メインのファイルが右側に表示されます。

このように、プロジェクトファイル.mqprojとメインのファイルさえ同じフォルダにおいておけば、問題なくコンパイルできます。

ただし、フォルダを移動すると、上記のプロジェクトファイルのプラットフォームの項目が、元通り、MetaTrader 5でコンパイルする設定に戻るようですので、プロジェクトファイルの一番上のプラットフォームをmt4に変えてください。コンパイルできれば、次に進みます。

3 サブファイルの追加

プロジェクトフォルダを作ったら、その中に、「Headers」というフォルダを作ります。そして、先ほどのメインファイルをコピーします。

メインファイルの名称をサブのファイル名称に変え、中身を書き換えます。

メインファイルMACD.mq4からの呼び出しですが、「Headers\」の下にサブルーチンのファイル名を付け、全体を””で囲みます。例えば、SeriesRefresh.mqhというサブファイルを参照する場合、以下のようにします。

#include “Headers\SeriesRefresh.mqh”

ここで、開いているファイルは、右欄の上にタブとして表示されます。例えば、SeriesRefresh.mqhというファイルをアクティブに表示させた場合、ここで、コンパイルのボタンをクリックしても、プロジェクト全体に対するコンパイルがかかります。

コンパイルした場合に、左のナビゲータにはheadersの中に勝手に登録されます。左のナビゲータのheadersの下のheadersが「+」になっていれば、それをクリックすると展開されファイルの一覧が出てきます。ファイルの登録の解除は、include文を削除したうえで、ナビゲータの右クリック、「削除」をクリックすると可能です。ヘッダを含めたプロジェクトファイルは、以上です。

変数の共有関係

#includeすることによって、メインファイルに読み込まれることによりプロジェクト全体が1つのファイルのように機能します。複数のファイル間で、グローバル変数(関数の外に記述した変数)は、ファイル間でも共有されます。これに対し、関数内で定義した変数は、ファイル間で共有されません。関数の引数や関数の返り値として渡す必要があります。

EAのプロジェクトファイル

EAのプロジェクトファイルについては、プロジェクトフォルダを、「Experts」フォルダにおいてください。

インジケータをEAに変える場合には、Onculcurate()という名称は削除してOnTick()に変え、インジケータの設定はすべて削除するなど、EAの体裁を整えてください。後々、EAへの変更については説明していきます。

ファンクションにおける変数渡しの基本

ファンクションでの変数渡しですが、配列を扱うことが多いので、簡単に説明しておきます。

グローバル変数で配列を定めたら、初期設定で配列の大きさを設定し(例えばArrayResize(MACD,bars);)、そのあと、サブ関数に処理を丸投げで委ねます。メインからサブへ渡すときは、値は空でもよく、「参照渡し」といって、サブ関数を実行したら、メイン関数が引数として渡したその配列に、サブ関数が値を格納し、その引数の配列の値が変容されてメインに戻ってきます。

メインの呼び出しは、例えば、「A=MACD(MACD,close・・・」、

サブの関数設定は、int MACD(double &MACD0[],double &close0[]・・・)となります。

サブのほうでは、型doubleと、参照渡しを示す&と配列を示す[]を付して関数を定義します。呼び出し側では、&や[]を付さないようにします。戻り値に配列が格納されるのではないので、戻り値はintで問題ありません。intの戻り値はエラーを返すのに使ったりします。Mt4では、参照渡しでないと配列を関数に渡せない決まりです。

なお、配列でない値の変数であっても、関数定義側で引数に&を付ければその変数は参照渡しになり、関数実行後にその呼び出した引数の変数の値が変更されます。

プロジェクトの生成方法の簡単な解説は、以上で終わりです。

3 mt4ファイルの名称ルール フレームワーク

どのような名称ルールでどのようにプロジェクトを生成すると合理的なのかについての当方の独自ルールを、以下の通り説明していきます。参考になれば幸いです。このようなルールを、ウェブプログラミングなどの世界ではフレームワークと呼ぶようです。CakePHPやLaravelなどがあります。

メインルーチンの一部として従属する機能には、「Part」という名称を付加する

ほかのプロジェクトでも使いまわす可能性がある機能を実装する場合は、「headers」以下のファイルにファンクションとしてまとめます。名称ルールですが、ファンクションについては私は特に何もルールは決めていないです。他方、メインルーチンの一部として、ファイル分割する場合には、「Part」という名称を付けることにしています。ファイル分割をする場合でも、変数を渡すことはできないので、グローバル変数を使い変数を渡します。名称の衝突などで問題ありで異論ありかもしれませんが、面倒すぎて変数全部を引数をつけて渡していられない場合、例えば、void InitPart(){}などという引数なし、返り値なしの関数を含むサブルーチンを、「Part」という名称をつけた、例えばMACDPart.mt4というファイルに記述し、ヘッダーファイル「Headers」に格納します。引数はないですが、グローバル変数に登録していれば、引数で値を渡さなくても、定義エラーにならず問題なく動作します。このプログラムでは、プログラムが回っている間、ずっと覚えている必要があるグローバル変数は多いですから、グローバル変数を使いまわしでもよいかと思います。このように記述した場合、グローバル変数はメインルーチンなどの別ファイルに書いてあるので、そのサブルーチン単体をコンパイルしても、変数定義がないというエラーが出て、コンパイルできません。

したがって、「Part」というのは、要するにそれ単体では動きません、コンパイルできませんという関数の名称を区別するものです。

初期化を定義するファイルやサブルーチンにはInitという名称を付加する

初期化を実行するファイルやサブルーチンには、Initという名称を付けています。初期化ルーチンはほとんどの場合、グローバル変数を処理し、ほかで使いまわすような独立した処理を行うことも必要もないので、メインに従属するものとして、「・・・InitPart.mqh」というファイルに「void ・・・InitPart(){}」というサブルーチンを組み立てます。この初期設定ルーチンはOnInit(){}から呼び出すようにします。

なお、最初だけ動作するプログラムの全部をOnInitやそのサブルーチンに書けばいいと思うかもしれません。しかし、オーバーフローなどで初期化に失敗するとそのインジケータ自体がmt4のシステムに登録されずに消えてしまい、原因の究明も難しくなります。また、コンパイルし直した場合にその都度インジケータがシステムから消えると面倒です。なので、OnInit()の処理は、オーバーフローの可能性がない最小限にとどめるべきです。そこで、initFlagなるものを設けて、int OnCalculate()メインのなかでも、一度きり行う処理も行います。OnInit()の中で、initFlagを1ないしtrueに設定し、int OnCalculate()の最後にinitFlagを0ないしfalseに設定すれば、一度きり行うバックテストの利益総額の計算などもメインのなかで簡単に行えます。回数を記録する変数を用意してその変数をインクリメントし、初回かどうかを判断するような方法だとその値がオーバーフローすることがありますが、上記の方法だとそういう心配も生じません。ただし、この場合のint OnCalculate()メインのなかで行う一度きりの処理には、Initという名称はつけていません。どちらかというとOnInit()内のサブルーチンとして行う場合には、Initという名称を付加します。

インジケータの初期化では、変数の初期化が毎度同じ処理で複数のコードが必要で、かなり面倒なので以下のような独自関数を作っています。

void ArrayResize_and_SetAsSeries(double &array[],int bars0)
{
ArrayResize(array,bars0);
ArraySetAsSeries(array,true);
}
void ArrayResize_and_SetAsSeries(datetime &array[],int bars0)
{
ArrayResize(array,bars0);
ArraySetAsSeries(array,true);
}

ArrayResizeとSetAsSeriesとが1行で済みます。オーバーレイといって、同じ関数で複数種類の変数(double、datetime)を扱えます。

パラメータとして利用する変数、インジケータ配列の設定、OnInit(),OnCalculate()といった骨格は、メインに記述する

好みもあると思いますが、パラメータとして利用する変数、インジケータの配列の設定、OnInit(),OnCalculate()といった骨格は、メインに記述するようにしています。その内部で、機能ごとにサブルーチンを呼び出します。ただし、パラメータとして利用する変数(Input int)でも、あるインジケータの情報を参照する場合には、後述のファイル構成のセットを考慮するとそのインジケータのファイルに記述したほうがいいかもしれません。

参入撤退条件は、骨格だけをメインに記述して、あとは、Tick1回あたりの動作は、ほかのファイルに委譲します。そうすることで、参入撤退位置のチャート表示と、EAの売買条件を1つの条件にまとめることができ、合理的な構成が可能になります。

付加ファイルの構成をグローバル変数定義、初期化、関数のセットでまとめる

例えば、とあるインジケータを参入撤退の条件として参考に入れる場合に、1つの付加ファイルを、グローバル変数定義、初期化、関数のセットでまとめるのがいいと思います。そうすれば、使いまわしがより容易になります。これは、いろいろなインジケータを試しに組み入れた場合に、だんだんとそのほうが良いと思うようになりました。

double MACD[];

int MACDInit();
{
ArrayResize_and_SetAsSeries(MACD,bars);
}

int MACD(double &MACD[],double &close0[],・・・・)
{
・・・
}

要するにclassのような考え方で、変数定義、初期化、関数を定義するやりかたです。

このファイルをincludeすると、ファイル内の関数の外にべた書きしている配列変数MACD[]は、グローバル変数として登録されます。MACDInit()は、OnInit()で呼び出すようにし、これにより配列変数の数が初期化されます。また、関数MACD()は、メインのルーチンの中で呼び出します。

ここで、上記の通り、パラメータとして利用する変数(Input int)でも、あるインジケータの情報を参照する場合には、このサブファイルに記述するのも、ありかもです。

関数定義の引数の変数には、0を付けてグローバル変数と区別する

関数定義の引数の変数には、グローバル変数と区別するため、例えば語尾に0を付して区別しています。

明示の方法で時系列変数の更新をするのが好ましい

例えば、上記のSeriesRefresh.mqhですが、終値の最近の値1~5を検出して変動があった場合には終値、インジケータの値を明示に更新をするようにし、システムの値配列の更新に依存しないようにします。mt4では確かにシリーズというか4本値の配列は自動更新されますし、インジケータ上の値も自動更新がかかります。しかし、動作がチャートに明示されないEAへの売買条件の移植を考えると、システムに依存したシフトを利用することによる不測の事態を、インジケータの作成段階からつぶしておく必要があり、そのように明示の更新をするようにしています。特に、iClose(NULL,5,i)というように別の時間軸の値を参照する場合、その時間軸で値の更新は自動ではされないので、その時間軸の終値の複数をtickごとに常時参照し、変動があれば、配列全部を更新するようにします。また、シリーズ変数に別の時間軸の変数を登録すると現在の時間ごとに自動更新というかシフトがかかり、値がめちゃくちゃになるということがありえます。それが上記の不測の事態に該当します。

なお、この更新では左へシフトなどといった安直な方法をとると、システムの再開時で時間が経過しておりデータがない領域があったり、上記のシフトによる値の破壊などが生じたりで、不安定になるので、定期的に全部を読み直すようにしています。

int SeriesRefresh(double &open0[],double &close0[],double &high0[],double &low0[],datetime &time0[],
int bars00,int TimeSpan0,int InitFlag0)
{
double close01,close02,close03,close04,close05;
//Alert(“flag=”,InitFlag0 );
close01= iClose(NULL,TimeSpan0,1);
close02= iClose(NULL,TimeSpan0,2);
close03= iClose(NULL,TimeSpan0,3);
close04= iClose(NULL,TimeSpan0,4);
close05= iClose(NULL,TimeSpan0,5);//初期化処理
if(InitFlag0==1)
{
for(int j=0; j<bars00; j++)
{
open0[j]= iOpen(NULL,TimeSpan0,j);
close0[j]= iClose(NULL,TimeSpan0,j);
low0[j]= iLow(NULL,TimeSpan0,j);
high0[j]= iHigh(NULL,TimeSpan0,j);
time0[j]=iTime(NULL,TimeSpan0,j);
}
// ★ここでは、 直ちにInitFlagを0にすることはしない。複数の時系列があるから。呼び出し側でフラッグを初期化する。
return 1;
}//Alert(InitFlag0);
//初期化フラグが0で値が変化しないときには以下の更新処理
if(InitFlag0==0
&&close01==close0[1]
&&close02==close0[2]
&&close03==close0[3]
&&close04==close0[4]
&&close05==close0[5])
{
open0[0]= iOpen(NULL,TimeSpan0,0);
close0[0]= iClose(NULL,TimeSpan0,0);
low0[0]= iLow(NULL,TimeSpan0,0);
high0[0]= iHigh(NULL,TimeSpan0,0);
time0[0]=iTime(NULL,TimeSpan0,0);
return 0;
}// Alert(“refresh”,” timeSpan=”,TimeSpan, “,close01=”,Close[1],” “,CloseL[1],”, close02=”,Close[2],” “,CloseL[2]);
//初期化フラグが0で、値が変化したときには以下の更新処理
for(int j=0; j<bars00; j++)
{
open0[j]= iOpen(NULL,TimeSpan0,j);
close0[j]= iClose(NULL,TimeSpan0,j);
low0[j]= iLow(NULL,TimeSpan0,j);
high0[j]= iHigh(NULL,TimeSpan0,j);
time0[j]=iTime(NULL,TimeSpan0,j); }
return 1;
}

時系列配列の数は、予め固定するのが好ましい

時系列配列の数であるbarsは、常時新しい値を読み込むのではなくて、初期設定時に読み込んだ値に固定し、配列の数の増加を防ぎ、オーバーフローの不安要因を予めつぶします。インジケータとして登録する変数は動的な配列変数で、本来はオーバーフローするはずがないですが、実際にはアレイサイズを登録していないとオーバーフローになります。また、そのバーの数を最初に固定しておかないと、上記の通りオーバーフローや動作不安定の原因となり、途中で動作が止まる可能性が出てきます。なので、インジケータとして登録するか否かにかかわらず、変数のバーの数を初期設定します。初期設定の順序は、まずは配列をインジケータとして登録したのちに、上記のArrayResizeを行います。

ただし、グローバル変数の定義でint bars=Barsと定義してしまうと、barsはBarsという変動する値のアドレスを読みに行くようでして、新規に定義したbars値が時間とともに変動してしまい、いつしか動作中にオーバーフローとなって停止するようです。なのでグローバル変数の定義ではint bars=0として0を入力して初期化し、OnInit()の中で一度だけbars=Barsを設定します。

また、多くのインジケータで引用している#include <MovingAverages.mqh>ですが、配列の平均化の計算において、途中でオーバーフローになることがあるので、独自関数を設けています。

なお、オーバーフローにより途中で処理が止まったように思えるときは、mt4本体のデフォルトで一番下の「ターミナル」のうち、「エキスパート」という項目に赤のしるしとともにエラー表示がなされます。エディタのどの場所がオーバーフローであるかも教えてくれます。

Follow me!

-mt4 fx自動売買 mql4
-

執筆者:

関連記事

no image

linuxの格安vps webarenaでmt4,セキュアにリモートデスクトップ(ubuntu,vnc,超軽量lxde利用):0導入編

目次 0 導入編 1 vpsであるwebarenaでインスタンスを作成し、ubuntuのターミナルに暗号化した通信ssh(Secure Shell)でログインする。 2 ubuntuのターミナルに暗号 …

no image

linuxの格安vps webarenaでmt4,セキュアにリモートデスクトップ(ubuntu,vnc,超軽量lxde利用)8 mt4をインストール

目次 0 導入編 1 vpsであるwebarenaでインスタンスを作成する。 2 ubuntuのターミナルに暗号化した通信ssh(Secure Shell)でログインする。 3 ubuntuのターミナ …

no image

linuxの格安vps webarenaでmt4,セキュアにリモートデスクトップ(ubuntu,vnc,超軽量lxde利用)6 ファイアーウォールの設定、ポート番号の変更をしてセキュリティを強化する

0 導入編 1 vpsであるwebarenaでインスタンスを作成する。 2 ubuntuのターミナルに暗号化した通信ssh(Secure Shell)でログインする。 3 ubuntuのターミナルから …

no image

linuxの格安vps webarenaでmt4,セキュアにリモートデスクトップ(ubuntu,vnc,超軽量lxde利用):1webarenaでインスタンス作成

目次 0 導入編 1 vpsであるwebarenaでインスタンスを作成する。 2 ubuntuのターミナルに暗号化した通信ssh(Secure Shell)でログインする。 3 ubuntuのターミナ …

no image

linuxの格安vps webarenaでmt4,セキュアにリモートデスクトップ(ubuntu,vnc,超軽量lxde利用):2 SSHでubuntuに接続

目次 0 導入編 1 vpsであるwebarenaでインスタンスを作成する。 2 ubuntuのターミナルに暗号化した通信ssh(Secure Shell)でログインする。 3 ubuntuのターミナ …

[最近の記事]
2022年4月
 12
3456789
10111213141516
17181920212223
24252627282930

3PR エリア3 タイトル

PAGE TOP