
ニューラルネットワークの実験(第2回):スマートなニューラルネットワークの最適化
はじめに
前回の「ニューラルネットワークの実験(第1回):幾何学の再検討」稿では、ニューラルネットワーク関連の観察・実験を共有しました。すなわち、どのようなデータをニューラルネットワークに渡せばよいのかという問題を考え、パーセプトロンを使って収益性の高い取引システムを得るという簡単な例を示したのです。成功した部分もありましたが、苦労した部分もあったので、ここでご紹介します。また、もっと複雑なニューラルネットワークに移行したいとも思っているので、そのために、「MQL言語を使用したゼロからのディープニューラルネットワークプログラミング」稿にあるライブラリを使ってみます。その動作原理は著者が詳細に述べているので、ここでは主要な点のみを取り上げることにします。本稿の主目的は、ニューラルネットワークを基盤とし、サードパーティ製ソフトウェアを使用せずにMetaTrader 5を使用する本格的な取引ロボットを開発することです。
基礎と実例
上記のライブラリの作者はディープニューラルネットワークを使っていますが、私は小さく始めて、4-4-3構造のネットワークを構築することを提案します。全部で(4 * 4) + 4 + (4 * 3) + 3 = 35個の重みとバイアスの値が必要です。
修正したライブラリは以下からダウンロードできます。カスタムニューラルネットワークの作り方がわかるように、コードの変更箇所はあえてすべてコメントアウトしています。
重量とバイアスの値は次の通りです。
input double w0=1.0; input double w1=1.0; input double w2=1.0; input double w3=1.0; input double w4=1.0; input double w5=1.0; input double w6=1.0; input double w7=1.0; input double w8=1.0; input double w9=1.0; input double w10=1.0; input double w11=1.0; input double w12=1.0; input double w13=1.0; input double w14=1.0; input double w15=1.0; input double b0=1.0; input double b1=1.0; input double b2=1.0; input double b3=1.0; input double x0=1.0; input double x1=1.0; input double x2=1.0; input double x3=1.0; input double x4=1.0; input double x5=1.0; input double x6=1.0; input double x7=1.0; input double x8=1.0; input double x9=1.0; input double x10=1.0; input double x11=1.0; input double x12=1.0; input double x13=1.0; input double x14=1.0; input double x15=1.0; input double s0=1.0; input double s1=1.0; input double s2=1.0;
WとXは重み、BとSはバイアスパラメータです。
ニューラルネットワークライブラリをインクルードします。
#include <DeepNeuralNetwork2.mqh> int numInput=4; int numHiddenA = 4; int numOutput=3; DeepNeuralNetwork dnn(numInput,numHiddenA,numOutput);
次に、前回の記事で紹介した2つの例、つまり傾斜角のある図形とパターンを考えてみることにします。ライブラリの著者の戦略の結果も見てみます。最後に、4-4-3と4-4-4-3という異なるニューラルネットワークで、これらすべてを確認します。 つまり、一度に6つのEAを開発することになります。
バタフライ(エンベロープ)を渡す、Figure EA:
int error=CandlePatterns(ind_In1[1], ind_In1[10], ind_In2[1], ind_In2[10], _xValues);// Call the function int CandlePatterns(double v1,double v2,double v3,double v4,double &xInputs[])// Function { xInputs[0] = (v1-v2)/Point(); xInputs[1] = (v3-v4)/Point(); xInputs[2] = (v1-v4)/Point(); xInputs[3] = (v3-v2)/Point(); return(1); }
スロープアングル(4つのMA 1とMA 24の指標スロープアングル)を持つパターンを渡す、Angle EA:
int error=CandlePatterns(ind_In1[1], ind_In1[5], ind_In1[10], ind_In2[1], ind_In2[5], ind_In2[10], _xValues);// Call the function int CandlePatterns(double v1,double v2,double v3,double v4,double v5,double v6,double &xInputs[])// Function { xInputs[0] = (((v1-v2)/Point())/5); xInputs[1] = (((v1-v3)/Point())/10); xInputs[2] = (((v4-v5)/Point())/5); xInputs[3] = (((v4-v6)/Point())/50); return(1); }
ストラテジーテスターを使用した後、重みとバイアスの最適化値を-1~1まで0.1刻みで設定します。3.68597592780611e+51のパスを取得します。次のセクションに進みます。
最適化の課題を解決する
上記のようにEAを使用する場合、ストラテジーテスターは「低速/完全のアルゴリズム」モードで1万回強のパスをおこないますが、ここでニューラルネットワークを最適化するには少なすぎます。「高速遺伝的アルゴリズム」モードは、ここでは役に立たないと思います。
主なアイデアは、パスに1つの変数だけを使用することで、これはある種のカウンタのようなものです。残りの重みとバイアスのパラメータは、EA内部で設定することになります。そのため、乱数発生器を使ってみることにしました。さらに、最適化ですでに使用したオプションを覚えておく必要があります。
以下は、乱数発生器関数です。
double RNDfromCI(double minp, double maxp) { if(minp == maxp) return (minp); double Minp, Maxp; if(minp > maxp) { Minp = maxp; Maxp = minp; } else { Minp = minp; Maxp = maxp; } return (NormalizeDouble(double(Minp + ((Maxp - Minp) * (double)MathRand() / 32767.0)),1)); }
パスのパラメータを記憶させるために、いくつかのオプションが検討されました。特に、EA内部の配列はEA初期化毎にゼロにリセットされるため、適していません。また、ターミナルのグローバル変数もデータ量が多いため、適していません。最適化の先にある完了したパスの結果を、データを読める状態で取り出すことが必要です。これにはCSVファイルを使うことにしました。
最適化のために1つの変数を導入してみましょう。
input int Passages = 10000;
Passagesパラメータを1から最大まで1刻みで最適化します。試しに、1変数でのモードでの最大パス数を調べてみてください。100,000,000に及びます。 これで十分かと思います。
当初、EAを最適化用と取引用の2つに分ける案もありましたが、1つなら便利だと思うので、モードスイッチを追加します。
input bool Optimization = true;
主な最適化コードは、EA初期化関数OnInit()にあります。
if (Optimization==true){ int r=0; int q=0; while(q!=1 && !IsStopped()) { string str; while(r!=1 && !IsStopped()) { sw0=DoubleToString(RNDfromCI(Min, Max),1); sw1=DoubleToString(RNDfromCI(Min, Max),1); sw2=DoubleToString(RNDfromCI(Min, Max),1); sw3=DoubleToString(RNDfromCI(Min, Max),1); sw4=DoubleToString(RNDfromCI(Min, Max),1); sw5=DoubleToString(RNDfromCI(Min, Max),1); sw6=DoubleToString(RNDfromCI(Min, Max),1); sw7=DoubleToString(RNDfromCI(Min, Max),1); sw8=DoubleToString(RNDfromCI(Min, Max),1); sw9=DoubleToString(RNDfromCI(Min, Max),1); sw10=DoubleToString(RNDfromCI(Min, Max),1); sw11=DoubleToString(RNDfromCI(Min, Max),1); sw12=DoubleToString(RNDfromCI(Min, Max),1); sw13=DoubleToString(RNDfromCI(Min, Max),1); sw14=DoubleToString(RNDfromCI(Min, Max),1); sw15=DoubleToString(RNDfromCI(Min, Max),1); sb0=DoubleToString(RNDfromCI(Min, Max),1); sb1=DoubleToString(RNDfromCI(Min, Max),1); sb2=DoubleToString(RNDfromCI(Min, Max),1); sb3=DoubleToString(RNDfromCI(Min, Max),1); sx0=DoubleToString(RNDfromCI(Min, Max),1); sx1=DoubleToString(RNDfromCI(Min, Max),1); sx2=DoubleToString(RNDfromCI(Min, Max),1); sx3=DoubleToString(RNDfromCI(Min, Max),1); sx4=DoubleToString(RNDfromCI(Min, Max),1); sx5=DoubleToString(RNDfromCI(Min, Max),1); sx6=DoubleToString(RNDfromCI(Min, Max),1); sx7=DoubleToString(RNDfromCI(Min, Max),1); sx8=DoubleToString(RNDfromCI(Min, Max),1); sx9=DoubleToString(RNDfromCI(Min, Max),1); sx10=DoubleToString(RNDfromCI(Min, Max),1); sx11=DoubleToString(RNDfromCI(Min, Max),1); ss0=DoubleToString(RNDfromCI(Min, Max),1); ss1=DoubleToString(RNDfromCI(Min, Max),1); ss2=DoubleToString(RNDfromCI(Min, Max),1); if(StringFind(sw0,".", 0) == -1 ){sw0=sw0+".0";} if(StringFind(sw1,".", 0) == -1 ){sw1=sw1+".0";} if(StringFind(sw2,".", 0) == -1 ){sw2=sw2+".0";} if(StringFind(sw3,".", 0) == -1 ){sw3=sw3+".0";} if(StringFind(sw4,".", 0) == -1 ){sw4=sw4+".0";} if(StringFind(sw5,".", 0) == -1 ){sw5=sw5+".0";} if(StringFind(sw6,".", 0) == -1 ){sw6=sw6+".0";} if(StringFind(sw7,".", 0) == -1 ){sw7=sw7+".0";} if(StringFind(sw8,".", 0) == -1 ){sw8=sw8+".0";} if(StringFind(sw9,".", 0) == -1 ){sw9=sw9+".0";} if(StringFind(sw10,".", 0) == -1 ){sw10=sw10+".0";} if(StringFind(sw11,".", 0) == -1 ){sw11=sw11+".0";} if(StringFind(sw12,".", 0) == -1 ){sw12=sw12+".0";} if(StringFind(sw13,".", 0) == -1 ){sw13=sw13+".0";} if(StringFind(sw14,".", 0) == -1 ){sw14=sw14+".0";} if(StringFind(sw15,".", 0) == -1 ){sw15=sw15+".0";} if(StringFind(sb0,".", 0) == -1 ){sb0=sb0+".0";} if(StringFind(sb1,".", 0) == -1 ){sb1=sb1+".0";} if(StringFind(sb2,".", 0) == -1 ){sb2=sb2+".0";} if(StringFind(sb3,".", 0) == -1 ){sb3=sb3+".0";} if(StringFind(sx0,".", 0) == -1 ){sx0=sx0+".0";} if(StringFind(sx1,".", 0) == -1 ){sx1=sx1+".0";} if(StringFind(sx2,".", 0) == -1 ){sx2=sx2+".0";} if(StringFind(sx3,".", 0) == -1 ){sx3=sx3+".0";} if(StringFind(sx4,".", 0) == -1 ){sx4=sx4+".0";} if(StringFind(sx5,".", 0) == -1 ){sx5=sx5+".0";} if(StringFind(sx6,".", 0) == -1 ){sx6=sx6+".0";} if(StringFind(sx7,".", 0) == -1 ){sx7=sx7+".0";} if(StringFind(sx8,".", 0) == -1 ){sx8=sx8+".0";} if(StringFind(sx9,".", 0) == -1 ){sx9=sx9+".0";} if(StringFind(sx10,".", 0) == -1 ){sx10=sx10+".0";} if(StringFind(sx11,".", 0) == -1 ){sx11=sx11+".0";} if(StringFind(ss0,".", 0) == -1 ){ss0=ss0+".0";} if(StringFind(ss1,".", 0) == -1 ){ss1=ss1+".0";} if(StringFind(ss2,".", 0) == -1 ){ss2=ss2+".0";} str=sw0+","+sw1+","+sw2+","+sw3+","+sw4+","+sw5+","+sw6+","+sw7+","+sw8+","+sw9+","+sw10+","+sw11+","+sw12+","+sw13+","+sw14+","+sw15 +","+sb0+","+sb1+","+sb2+","+sb3 +","+sx0+","+sx1+","+sx2+","+sx3+","+sx4+","+sx5+","+sx6+","+sx7+","+sx8+","+sx9+","+sx10+","+sx11 +","+ss0+","+ss1+","+ss2; if (VerifyPassagesInFile(str)==true) { ResetLastError(); filehandle = FileOpen(OptimizationFileName,FILE_WRITE|FILE_READ|FILE_CSV|FILE_COMMON|FILE_ANSI, ";"); if(filehandle!=INVALID_HANDLE) { Print("FileOpen OK"); FileSeek(filehandle, 0, SEEK_END); FileWrite(filehandle,Passages,sw0,sw1,sw2,sw3,sw4,sw5,sw6,sw7,sw8,sw9,sw10,sw11,sw12,sw13,sw14,sw15, sb0,sb1,sb2,sb3, sx0,sx1,sx2,sx3,sx4,sx5,sx6,sx7,sx8,sx9,sx10,sx11, ss0,ss1,ss2); FileClose(filehandle); weight[0]=StringToDouble(sw0); weight[1]=StringToDouble(sw1); weight[2]=StringToDouble(sw2); weight[3]=StringToDouble(sw3); weight[4]=StringToDouble(sw4); weight[5]=StringToDouble(sw5); weight[6]=StringToDouble(sw6); weight[7]=StringToDouble(sw7); weight[8]=StringToDouble(sw8); weight[9]=StringToDouble(sw9); weight[10]=StringToDouble(sw10); weight[11]=StringToDouble(sw11); weight[12]=StringToDouble(sw12); weight[13]=StringToDouble(sw13); weight[14]=StringToDouble(sw14); weight[15]=StringToDouble(sw15); weight[16]=StringToDouble(sb0); weight[17]=StringToDouble(sb1); weight[18]=StringToDouble(sb2); weight[19]=StringToDouble(sb3); weight[20]=StringToDouble(sx0); weight[21]=StringToDouble(sx1); weight[22]=StringToDouble(sx2); weight[23]=StringToDouble(sx3); weight[24]=StringToDouble(sx4); weight[25]=StringToDouble(sx5); weight[26]=StringToDouble(sx6); weight[27]=StringToDouble(sx7); weight[28]=StringToDouble(sx8); weight[29]=StringToDouble(sx9); weight[30]=StringToDouble(sx10); weight[31]=StringToDouble(sx11); weight[32]=StringToDouble(ss0); weight[33]=StringToDouble(ss1); weight[34]=StringToDouble(ss2); r=1; q=1; } else { Print("FileOpen ERROR, error ",GetLastError()); q=0; } } } } }
コードを説明します。EAに「ファイルを開く順番を待つ」ことを教えるのは、とてもエキサイティングな作業でした。複数のCPUコアを同時に使用しながら、最適化モードで1つのファイルを同時に開くという問題に遭遇しましたが、この問題は、最初のwhileループで解決されました。EAは、ファイルが書き込み用に開かれるまで、OnInit()関数を終了しません。最適化の際にEAが次々と起動されることが判明しました。
1番目のコアでテストがおこなわれている間、EAは2番目のコアで書き込みのためのファイルを開くことができます。さらに、すべての重みとバイアスにMinとMaxの範囲のランダムなパラメータを割り当てます。数値が整数になった場合は、.0を足します。すべての値を1つのstr文字列に追加します。VerifyPassagesInFile(str)関数で、ファイル内に同じ文字列があるかどうかを確認します。ない場合は、ファイルの末尾に書き込み、weight[]配列に得られた重みとバイアスのランダムな値を入力します。
次は、パラメータが前のパラメータと類似しているかどうかを確認する関数のコードです。
bool VerifyPassagesInFile(string st){ string str=""; string str1=""; string str2=""; string str3=""; string str4=""; string str5=""; string str6=""; string str7=""; string str8=""; string str9=""; string str10=""; string str11=""; string str12=""; string str13=""; string str14=""; string str15=""; string str16=""; string str17=""; string str18=""; string str19=""; string str20=""; string str21=""; string str22=""; string str23=""; string str24=""; string str25=""; string str26=""; string str27=""; string str28=""; string str29=""; string str30=""; string str31=""; string str32=""; string str33=""; string str34=""; string str35=""; string str36=""; if (FileIsExist(OptimizationFileName)== true ){ ResetLastError(); filehandle = FileOpen(OptimizationFileName,FILE_WRITE|FILE_READ|FILE_CSV|FILE_COMMON|FILE_ANSI, ";"); if(filehandle!=INVALID_HANDLE) { Print("FileCreate OK"); } else Print("FileCreate ERROR, error ",GetLastError()); FileClose(filehandle); } ResetLastError(); filehandle = FileOpen(OptimizationFileName,FILE_WRITE|FILE_READ|FILE_CSV|FILE_COMMON|FILE_ANSI, ";"); if(filehandle!=INVALID_HANDLE) { Print("FileOpen OK"); } else Print("FileOpen ERROR, error ",GetLastError()); //--- read the data from the file while(!FileIsEnding(filehandle) && !IsStopped()) { str1=FileReadString(filehandle); str2=FileReadString(filehandle); str3=FileReadString(filehandle); str4=FileReadString(filehandle); str5=FileReadString(filehandle); str6=FileReadString(filehandle); str7=FileReadString(filehandle); str8=FileReadString(filehandle); str9=FileReadString(filehandle); str10=FileReadString(filehandle); str11=FileReadString(filehandle); str12=FileReadString(filehandle); str13=FileReadString(filehandle); str14=FileReadString(filehandle); str15=FileReadString(filehandle); str16=FileReadString(filehandle); str17=FileReadString(filehandle); str18=FileReadString(filehandle); str19=FileReadString(filehandle); str20=FileReadString(filehandle); str21=FileReadString(filehandle); str22=FileReadString(filehandle); str23=FileReadString(filehandle); str24=FileReadString(filehandle); str25=FileReadString(filehandle); str26=FileReadString(filehandle); str27=FileReadString(filehandle); str28=FileReadString(filehandle); str29=FileReadString(filehandle); str30=FileReadString(filehandle); str31=FileReadString(filehandle); str32=FileReadString(filehandle); str33=FileReadString(filehandle); str34=FileReadString(filehandle); str35=FileReadString(filehandle); str36=FileReadString(filehandle); str=str2+","+str3+","+str4+","+str5+","+str6+","+str7+","+str8+","+str9+","+str10+","+str11 +","+str12+","+str13+","+str14+","+str15+","+str16+","+str17+","+str18+","+str19+","+str20+","+str21 +","+str22+","+str23+","+str24+","+str25+","+str26+","+str27+","+str28+","+str29+","+str30+","+str31+","+str32+","+str33+","+str34+","+str35+","+str36; if (str==st){FileClose(filehandle); return(false);} } FileClose(filehandle); return(true); }
ここではまず、FileIsExist(OptimizationFileName)ファイルが存在するかどうかを確認し、 ない場合は作成します。次に、while(!FileIsEnding(filehandle) && !IsStopped())ループで文字列をstr1変数からstrN変数までに読み込んで、すべてをまとめてstr変数に入れます。各str文字列を、関数入力で入力されるst文字列と比較します。最後に、一致するかどうかの結果を返します。
次は、結果としてのEAパラメータです。
------------開くときの設定------------
Optimization - モード切り替え(最適化または取引)
Min - 重みとバイアスのパラメータの最小値
Max - 重みとバイアスのパラメータの最大値
OptimizationFileName - 最適化中にパラメータを書き込み、取引モードで読み込むためのファイル名
Passages - パスの値。最適化パラメータを1~最大100,000,000まで、1刻みで指定
LL - Softmax関数により、100%の和に基づいた3つの結果を提供(0.6は60%以上の数値に相当)
------------ロット設定------------
SetFixLotOrPercent - ロットまたは預金のパーセンテージを選択
LotsOrPercent - SetFixLotOrPercentに依存するロットまたはパーセンテージ
------------その他の設定------------
MaxSpread - 注文を開始する際の最大スプレッド
Slippage - スリッページ
Magic - マジックナンバー
EAComment - 注文に対するコメント
ご存知のように、MetaTrader 5はデフォルトでサンドボックス内にファイルを作成します。
以下はファイルへのパスです。
C:\Users\<ユーザ名>\AppData\Roaming\MetaQuotes\Terminal\Common\Files
最適化
最適化を実行しましょう。期間:2018.07.12~2021.07.12、EURUSDH1、始値、テスト実行数:10,000.
モード:Complex Criterion max、Angle EA 4-4-3
モード:Complex Criterion max、Angle EA 4-4-4-3
モード:Complex Criterion max、Figure EA 4-4-3
モード:Complex Criterion max、Figure EA 4-4-4-3
モード:Complex Criterion max、オリジナルのEA 4-4-3。ライブラリの作者の戦略。
モード:Complex Criterion max、Original EA 4-4-4-3、ライブラリの作者の戦略
最適化(例えば10,000パス)の後、新しい最適化を開始することで、ファイルに新しいパラメータを入力し続けることができます。ストラテジーテスターの最適化レポートには、あらかじめ必要なPassagesパラメータを保存しておくことを忘れないようにしてください。また、ターミナルの履歴を消去しておかないと、テスターに既に終了した最適化の結果が表示されてしまいます。私はこれを.batタイプのスクリプトでおこなっています。
del C:\Users\<ユーザ名>\AppData\Roaming\MetaQuotes\Terminal\36A64B8C79A6163D85E6173B54096685\Tester\cache\*.* /q /f /s
for /d %%i in (C:\Users\<ユーザ名>\AppData\Roaming\MetaQuotes\Terminal\36A64B8C79A6163D85E6173B54096685\Tester\cache\*) do rd /s /q "%%i"
del C:\Users\<ユーザ名>\AppData\Roaming\MetaQuotes\Terminal\36A64B8C79A6163D85E6173B54096685\Tester\logs\*.* /q /f /s
for /d %%i in (C:\Users\<ユーザ名>\AppData\Roaming\MetaQuotes\Terminal\36A64B8C79A6163D85E6173B54096685\Tester\logs\*) do rd /s /q "%%i"
<ユーザ名>と<36A64B8C79A6163D85E6173B54096685>を自分のものに置き換えてください。通常のテキストエディターで開くことができます。クリーンアップスクリプトを以下に添付します。
最適化結果の活用
最適化の結果を確認するには、Optimizationスイッチを「false」に設定し、Passagesパラメータに必要なパスインデックスを設定します。そのためには、必要な結果をダブルクリックするだけです。
以下は、テスト用の重みとバイアスのパラメータをアップロードするためのコードです。
if (FileIsExist(OptimizationFileName)==false){ int id = 0; int i = 0; string f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18,f19,f20,f21,f22,f23,f24,f25,f26,f27,f28,f29,f30,f31,f32,f33,f34,f35,f36; int handle = FileOpen(OptimizationFileName,FILE_WRITE|FILE_READ|FILE_CSV|FILE_COMMON|FILE_ANSI, ";"); if(handle!=INVALID_HANDLE) {Print("Loading optimization file."); while(!FileIsEnding(handle) && !IsStopped()) { f1=FileReadString(handle); f2=FileReadString(handle); f3=FileReadString(handle); f4=FileReadString(handle); f5=FileReadString(handle); f6=FileReadString(handle); f7=FileReadString(handle); f8=FileReadString(handle); f9=FileReadString(handle); f10=FileReadString(handle); f11=FileReadString(handle); f12=FileReadString(handle); f13=FileReadString(handle); f14=FileReadString(handle); f15=FileReadString(handle); f16=FileReadString(handle); f17=FileReadString(handle); f18=FileReadString(handle); f19=FileReadString(handle); f20=FileReadString(handle); f21=FileReadString(handle); f22=FileReadString(handle); f23=FileReadString(handle); f24=FileReadString(handle); f25=FileReadString(handle); f26=FileReadString(handle); f27=FileReadString(handle); f28=FileReadString(handle); f29=FileReadString(handle); f30=FileReadString(handle); f31=FileReadString(handle); f32=FileReadString(handle); f33=FileReadString(handle); f34=FileReadString(handle); f35=FileReadString(handle); f36=FileReadString(handle); if (StringToInteger(f1)==Passages){ weight[0]=StringToDouble(f2); Print(weight[0]); weight[1]=StringToDouble(f3); Print(weight[1]); weight[2]=StringToDouble(f4); Print(weight[2]); weight[3]=StringToDouble(f5); weight[4]=StringToDouble(f6); weight[5]=StringToDouble(f7); weight[6]=StringToDouble(f8); weight[7]=StringToDouble(f9); weight[8]=StringToDouble(f10); weight[9]=StringToDouble(f11); weight[10]=StringToDouble(f12); weight[11]=StringToDouble(f13); weight[12]=StringToDouble(f14); weight[13]=StringToDouble(f15); weight[14]=StringToDouble(f16); weight[15]=StringToDouble(f17); weight[16]=StringToDouble(f18); weight[17]=StringToDouble(f19); weight[18]=StringToDouble(f20); weight[19]=StringToDouble(f21); weight[20]=StringToDouble(f22); weight[21]=StringToDouble(f23); weight[22]=StringToDouble(f24); weight[23]=StringToDouble(f25); weight[24]=StringToDouble(f26); weight[25]=StringToDouble(f27); weight[26]=StringToDouble(f28); weight[27]=StringToDouble(f29); weight[28]=StringToDouble(f30); weight[29]=StringToDouble(f31); weight[30]=StringToDouble(f32); weight[31]=StringToDouble(f33); weight[32]=StringToDouble(f34); weight[33]=StringToDouble(f35); weight[34]=StringToDouble(f36); FileClose(handle); break; } } FileClose(handle); } else{ PrintFormat("Could not open file %s, error code = %d",OptimizationFileName,GetLastError()); return(INIT_FAILED); } }else{ PrintFormat("Could not open file %s, error code = %d",OptimizationFileName,GetLastError()); return(INIT_FAILED); }
Optimizationスイッチが無効のときにファイルを読み込みます。最初の列の値をPassagesパラメータの値と比較します。一致するものがあれば,weight[]配列に重みとバイアスのパラメータを代入します。.こうすれば、最適な結果をテストすることができます。
得られた結果をフォワードテストして、ベスト3を見つけます。私の場合、最大利益率と取引回数が100回以上であることを選択基準にしています。
- テスト間隔:2021.07.12~2022.07.12
- モード:実際のティックに基づいたすべてのティック
- 初期デポジット:10,000
- 時間枠:H1
- 固定ロット:0.01
- アングルEA4-4-3
テスト1:
Test 2
Test 3
- テスト間隔:2021.07.12~2022.07.12
- モード:実際のティックに基づいたすべてのティック
- 初期デポジット:10,000
- 時間枠:H1
- 固定ロット:0.01
- Angle EA 4-4-4-3
テスト1:
Test 2
Test 3
- テスト間隔:2021.07.12~2022.07.12
- モード:実際のティックに基づいたすべてのティック
- 初期デポジット:10,000
- 時間枠:H1
- 固定ロット:0.01
- Figure EA 4-4-3
テスト1:
テスト2
テスト3
- テスト間隔:2021.07.12~2022.07.12
- モード:実際のティックに基づいたすべてのティック
- 初期デポジット:10,000
- 時間枠:H1
- 固定ロット:0.01
- Figure EA 4-4-4-3
テスト1:
テスト2
テスト3
次に、ニューラルネットワーク4-4-3、4-4-4-3でライブラリ作者のオリジナル戦略を検証します。
- テスト間隔:2021.07.12~2022.07.12
- モード:実際のティックに基づいたすべてのティック
- 初期デポジット:10,000
- 時間枠:H1
- 固定ロット:0.01
- オリジナルのEA 4-4-3
テスト1:
テスト2
テスト3
- テスト間隔:2021.07.12~2022.07.12
- モード:実際のティックに基づいたすべてのティック
- 初期デポジット:10,000
- 時間枠:H1
- 固定ロット:0.01
- Original EA 4-4-4-3
テスト1:
テスト2
テスト3
結果として、「Figure EA 4-4-3」や「Figure EA 4-4-4-3」よりも「Angle EA 4-4-3」や「Angle EA 4-4-4-3」のストラテジーがうまく機能しました。その理由は、市場分析に非標準的なアプローチを取っていることにあると思います。2コアでの最適化は、3年間で20分程度です。また、実験後、様々な疑問が生じたので、その解決も必要です。
- 長期間で最適化をおこなう
- パス回数を増やす
- 今後に向けた最適な戦略を決定する
- 取引最適化結果のあるデータベースを同時に扱うアルゴリズムを開発する(すでに考え始めています)
- 最適化と取引を同時におこなうアルゴリズムを開発する
- 時間軸を使い分け、最適な結果を導き出す
- 異なるデータセットを持つ2つ以上のニューラルネットワークを1つのEAで結合する
もちろん、フルテストにはよりディープな訓練が必要ですが、得られた結果はそれを物語っています。また、このような実験には大きな計算機資源が必要です。
結論
今回は、より複雑なニューラルネットワークに話を移しました。ニューラルネットワークに渡すべき必要なデータを特定するために、多くの研究がなされてきました。ただし、その可能性はそれだけにとどまりません。より多くの指標からデータを渡すようにしたり、複雑なリンクを使ったりする必要があります。これが、収益性の高い取引ロボットの開発に新たな成功がもたらされるきっかけになることを期待します。MetaTrader 5は、サードパーティのソフトウェアがなくても大丈夫なことがわかりました。さらに、非常に興味深い最適化アルゴリズムを開発し、能力を拡大することに成功しました。
いつも通り、より深い最適化とフォワードテストの実行は、皆さんにお任せすることにします。
添付ファイル:
Angle EA 4-4-3 - МA1 and МA24 indicator angle slopes strategy, neural network 4-4-3. Angle EA 4-4-4-3 - МA1 and МA24 indicator angle slopes strategy, neural network 4-4-4-3. Figure EA 4-4-3 - МA1 and МA24 indicator butterfly (envelope) strategy, neural network 4-4-3. Figure EA 4-4-4-3 - МA1 and МA24 indicator butterfly (envelope) strategy, neural network 4-4-4-3. Original EA 4-4-3 - candle size percentage strategy, neural network 4-4-3. Original EA 4-4-4-3 - candle size percentage strategy, neural network 4-4-4-3. Clear.bat - script for cleaning terminal files (Log and Cache). DeepNeuralNetwork – 4-4-4-3 neural network library. DeepNeuralNetwork2 – 4-4-3 neural network library.
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/11186





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索