
時系列予測に対する ENCOG マシン学習へのMetaTrader 5インディケータ使用
はじめに
本稿では MetaTrader 5 の ENCOGへの連携をご紹介します。 これは発展したニューラルネットワークとマシン学習のフレームワークで、Heaton Researchによって開発されたものです。 MetaTraderが、FANN、NeuroSolutions、Matlab、NeuroShellといったマシン学習テクニックを可能にするメソッドについては以前にも述べられています。ENCOG は優秀でうまくデザインされているので、無料のソリュー―ションであればよいと思います。
それでは私がENCOGを選んだ理由は何でしょうか?理由はいくつかあります。
- ENCOG は別に2件の商業的トレーディングソフトウェアパッケージに使用されています。ひとつはC#を基に、もう一つは JAVAに基づくものです。これは、ENCOGが金融の時系列データ予測に関してすでに検証されていることを意味します。
- ENCOG は無料でオープンソースのソフトウェアです。ニューラルネットワーク内の現状を知りたければ、ソースコードを見ることができます。これは時系列予測問題の一部を理解するためにしたことです。C# は明快で理解しやすいプログラム言語です。
- ENCOG はひじょうによく文書化されています。Heaton Research の創設者であるHeaton氏は将来データ予測にNCOGを用いたニューラルネットワーク、マシーン学習無料のオンラインコースを提供されています。私自身、本稿執筆前には氏のレッスンを多く参照しました。人工ニューラルネットワークを理解するのにたいへん役立ちました。また、Heaton Research ウェブサイトにはEJAVA やC#でNCOG をプログラムすることに関する電子書籍があります。ENCOG ドキュメンテーションの完全版は オンラインで入手可能です。
- ENCOG は忘れ去られたプロジェクトではありません。本稿執筆中もENCOG 2.6 はまだ開発中でした。ENCOG 3.0 ロードマップも最近発売されたばかりです。
- ENCOG は堅牢です。設計もよくマルチ CPU コアで、ニューラルネットワーク計算のスピードアップのためマルチスレッドです。コードの一部は OpenCL向けにポートを開始し、GPU に計算を可能にしました。
- ECNOG が現在サポートする機能は以下です。
マシーン学習タイプ
- フィードフォワード および シンプルリカレント (Elman/Jordan)
- 遺伝的アルゴリズム
- NEAT
- 確率的ニューラルネットワーク/標準回帰ニューラルネットワーク (PNN/GRNN)
- 自己組織化マップ (SOM/Kohonen)
- シミュレーションされたアニーリング
- ベクターマシンをサポート
ニューラルネットワークアーキテクチャ
トレーニング技術
- ADALINE ニューラルネットワーク
- 適応共鳴理論1 (ART1)
- 双方向連想記憶 (BAM)
- ボルツマンマシン
- 対向伝搬ニューラルネットワーク (CPN)
- リカレントニューラルネットワーク(エルマン型)
- フィードフォワードニューラルネットワーク (パーセプトロン)
- ホップフィールド型ニューラルネットワーク
- リカレントニューラルネットワーク(ジョーダン型)
- 引数トポロジーのニューロエボルーション (NEAT)
- 動径基底関数ネットワーク
- リカレント自己組織化マップ (RSOM)
- 自己組織化マップ (Kohonen)
アクティブ化関数
- 逆伝搬
- レシリエントプロパゲーション (RPROP)
- Scaled Conjugate Gradient (SCG)
- マンハッタンアップデートルールプロパゲーション
- コンペティティブ学習
- ホップフィールド学習
- Levenberg-Marquardt アルゴリズム (LMA)
- 遺伝的アルゴリズムトレーニング
- Instar トレーニング
- Outstar トレーニング
- ADALINE トレーニング
- トレーニングデータモデル
- 監視あり
- 監視無
- テンポラル(予測)
- 金融(Yahooファイナンスからダウンロード)
- SQL
- XML
- CSV
- 画像ダウンサンプル
無作為化テクニック
- Competitive
- Sigmoid
- Hyperbolic
- リニア
- SoftMax
- Tangential
- Sin Wave
- Step
- Bipolar
- Gaussian
- 四捨五入範囲
- Gaussian 乱数
- Fan-In
- Nguyen-Widrow
計画機能
- HyperNEAT
- 制約あるボルツマンマシーン (RBN/Deep Belief)
- ニューラルネットワークのスパイキング
ご覧のとおりかなり長い機能リストです。
本序論的記事はレシリエントプロパゲーション (RPROP) トレーニングを伴うフォワードニューラルネットワークのフィードに着目しています。また、データ準備の基本も取り上げています。一時的時系列予測のためのタイムボックスと正規化
本稿執筆についての知識は Heaton Researchウェブサイトで入手可能な指導書を基にしており、それは NinjaTraderにおける金融事件列の予測に関する直近のものです。ENCOG は JAVA および C# を基本にしていることに注意が必要です。本稿は私の前稿『アンマネージドのエクスポートによるC# コードの MQL5 へのエクスポーズ』なしでは執筆できませんでした。このソリュー―ションは Metatrader 5 インディケータと ENCOG 時系列予測のかけ橋としてC# DLLの使用を可能にするものです。
1. ニューラルネットワークのへのインプットとしてのテクニカルインディケータ値の使用
人工のニューラルネットワークは、脳の神経ネットワークをまねようとした人間工学アルゴリズムです。
使用可能はニューラルアルゴリズムにはさまざまなタイプがあり、多様なニューラルネットワークアーキテクチャが存在します。研究分野はかなり広いので単一タイプのニューラルネットワークに対する専門の本はあらゆるものがあります。本稿ではそういった詳細情報を扱うものではないので、Heaton Research 指導書やそのテーマを扱った著書を読まれることをお薦めするにとどめます。フィード前方ニューラルネットワークのインプットおよびアウトプットに注目し、金融時系列予測の実例をお話するようにします。
金融時系列の予想を始めるにあたり、ニューラルネットワークに提供するべきもの、そこから受け取るものについて考える必要があります。抽象的なブラックボックス指向では、既定の証券契約のロングまたはショートポジションを取ることで収益や損失を産み、いくらか時間が経過したところで取り引きを終了します。
過去の証券価格やテクニカルインディケータ値を観測することで、承認を売り買いするための将来のセンチメントや価格方向予想し、判断がコインを投げて得られるものではないと確信しようとします。状況はおおよそ下図のようなものです。
図1 テクニカルインディケータを用いた金融時系列予測
同じことを人工知能を使用して行います。ニューラルネットワークにはインディケータ値を認識し、価格が上下する可能性があるかどうか判断します。それにはどうするのでしょうか? フィードフォワード ニューラルネットワークアーキテクチャを使用して金融時系列の予測を行うので、そのアーキテクチャに対してイントロダクションを作成する必要があると思います。
フィードフォワードニューラルネットワークは階層にグループ化されたニューロンで構成されています。最小2階層が必要です。インプットニューロンが存在するインプット階層とアウトプットニューロンが存在するアウトプット階層です。またインプットとアウトプットの階層の間には隠れた階層が存在します。インプット階層はシンプルにダブル値配列、アウトプット階層はダブル値配列を形成する1個以上のニューロンで構成されます。下図で確認します。
図2 フィードフォワードのニューラルネットワーク階層
ニューロン間の結合は図をシンプルにするため描画していません。インプット階層の各ニューロンは隠れ階層のニューロンに結合しています。隠れ階層からのニューロンはそれぞれアウトプット階層のニューロンに結合しています。
各結合にはおのおののウェイトがあります。それもダブル値で閾値を持つアクティブ化関数です。それはニューロンをアクティブにし、情報を次のニューロンに渡します。これが「フィードフォワード」ネットワークと呼ばれるゆえんです。アクティブなニューロンのアウトプットに基づく情報はニューロンのある階層から別の階層へ前方に渡されるのです。フィードフォワードニューラルネットワークを詳しく紹介したビデオは次のリンクで見ることができます。
- ニューラルネットワーク計算(パート1):フィードフォワードストラクチャ
- ニューラルネットワーク計算(パート2):アクティブ化関数&基本計算
- ニューラルネットワーク計算(パート3):フィードフォワードニューラルネットワーク計算
ニューラルネットワークアーキテクチャとそのメカニズムについて学習したあと、まだ混乱しているかもしれません。
その主な理由は以下です。
- ニューラルネットワークに渡すデータはどんなものか?
- それをどのように渡すのか?
- ニューラルネットワーク向けのインプットデータの準備方法は?
- ニューラルネットワークアーキテクチャの選び方は?インプットニューロン、隠れニューロン、アウトプットニューロンはいくつ必要か?
- ネットワークのトレーニング方法は?
- アウトプットに何を期待するのか?
2. どんなデータをニューラルネットワークに渡すのか
インディケータのアウトプットを基にファイナンシャル予測をするので、ネットワークにはインディケータのアウトプット値を渡す必要があります。本稿のために、インプットとしてストキャスティック%K、 ストキャスティック スロー %D、ウィリアムズ %Rを選択しました。
図3 予測に使用するテクニカルインディケータ
インディケータ値を抽出するために、関数iStochastic および iWPR MQL5 を使用します。
double StochKArr[], StochDArr[], WilliamsRArr[]; ArraySetAsSeries(StochKArr, true); ArraySetAsSeries(StochDArr, true); ArraySetAsSeries(WilliamsRArr, true); int hStochastic = iStochastic(Symbol(), Period(), 8, 5, 5, MODE_EMA, STO_LOWHIGH); int hWilliamsR = iWPR(Symbol(), Period(), 21); CopyBuffer(hStochastic, 0, 0, bufSize, StochKArr); CopyBuffer(hStochastic, 1, 0, bufSize, StochDArr); CopyBuffer(hWilliamsR, 0, 0, bufSize, WilliamsRArr);
このコードが実行されたら、3つの配列、StochKArr、StochDArr、 WilliamsRArr にインディケータのアウトプット値を書き込む必要があります。トレーニングサンプルのサイズに応じて、値は数千におよぶ可能性があります。これら2つのインディケータは教育的目的で選択されていることにご注意ください。
予測に適するとおもうインディケータはそんなものでも実験できます。株式指数を予測するために金や石油価格をネットワークに渡したいと思うかもしれません。また、相関な外為通貨ペアを使って別の通貨ペアを予測するかもしれません。
3. タイムボックスインプットデータ
複数のインディケータからインプットデータを収集し、インプットをニューラルネットワークに渡す前に「タイムボックス」する必要があります。タイムボックスはネットワークに対する現在インプットをデータの移動スライスとする技術です。インプットデータの移動ボックスが時間軸上で前に移動するのがイメージできると思います。これには2段階の手順があります。
1. 各インディケータバッファからインプットデータを収集します。開始ポジションから将来に向かってエレメント数INPUT_WINDOW をコピーする必要があります。インプットウィンドウは予測に使用するバー数です。
図4 インディケータバッファからのインプットデータ収集
上の例にあるように、INPUT_WINDOW は4つのバーに等しく、エレメントを I1 配列にコピーしました。I1[0] は最初のエレメントで、 I1[3] は最後のエレメントです。同様にデータは別のインディケータから INPUT_WINDOW サイズ配列にコピーする必要があります。この図は 時系列 配列に対して trueに設定される AS_SERIES flag を伴い有効です。
2. INPUT_WINDOW 配列をニューラルネットワークインプット階層に渡される配列にコンバインします。
図5 タイムボックスインプットウィンドウ配列
3つのインディケータたあります。まず、インディケータの最初の値を取り、二番目の値を取ります。そして上の図のようにインプットウィンドウがいっぱいになるまで繰り返します。インディケータアウトプットからコンバインされたそのようは配列はニューラルネットワークのインプット階層に渡されます。新規バーがやってきたら、データは1エレメント分スライスされ全プロシージャが準備完了です。予測データ準備に関する詳細にご興味がある場合は、この話題を扱った ビデオを見てください 。
4. 正規化インプットデータ
ニューラルネットワークを効果的にするにはデータを正規化します。これはアクティブ化関数の計算を修正するのに必要です。正規化はデータを 0..1 また -1..1という範囲に変換する数学的処理です。正規化データは再正規化が可能です。すなわち、元の範囲に変換し戻すことができるのです。
再正規化はニューラルネットワークのアウトプットを人間が解読できる形式にデコードするのに必要です。さいわい、ENCOG は正規化および再正規化に配慮があるため、規化および再正規化は実装の必要がありません。それがどのように動作するかご興味があれば、以下のコードを分析してみるとよいでしょう。
/** * Normalize the specified value. * @param value The value to normalize. * @return The normalized value. */ public static double normalize(final int value) { return ((value - INPUT_LOW) / (INPUT_HIGH - INPUT_LOW)) * (OUTPUT_HIGH - OUTPUT_LOW) + OUTPUT_LOW; } /** * De-normalize the specified value. * @param value The value to denormalize. * @return The denormalized value. */ public static double deNormalize(final double data) { double result = ((INPUT_LOW - INPUT_HIGH) * data - OUTPUT_HIGH * INPUT_LOW + INPUT_HIGH * OUTPUT_LOW) / (OUTPUT_LOW - OUTPUT_HIGH); return result; }
また、より詳しく正規化について述べた記事を読むのもよいでしょう 。
5. ネットワークアーキテクチャとニューロン数の選択
本題材に関して初心者の方にとっては、正しいネットワークアーキテクチャの選択は難しい部分です。本稿では。フィードフォワードニューラルネットワークアーキテクチャを、インプット階層、隠れ階層、アウトプット階層の3階層に制限しました。もっと数多くの階層で試してみるのも自由です。
インプット、アウトプット階層については、必要とするニューロン数を正確に数えることができます。隠れ階層については、前方選択アルゴリズムを用いてニューラルネットワークエラーを最小にするようにします。別のメソッドを使うのもおすすめです。ニューロン計算の遺伝的アルゴリズムもいくらかあろうかと思います。
ENCOG で使用される別手法は後方選択アルゴリズム またはプルーニングと呼ばれ、基本的に階層とウェイトゼロの結合で消去される隠れニューロンの間の結合を判定するものですが、それも試してみたいとお思いかもしれません。
5.1. インプットニューロン階層
タイムボックスのおかげで、インプット階層のニューロン数は次のバーを予測するのに使われるバーの数×インディケータ数に一致するはずです。インプットとして3個インディケータを使用し、インプットウィンドウサイズが6バーに等しければ、インプット階層には18ニューロンあるということになります。インプット階層はタイムボックスで準備されたデータを受け取ります。
5.2. 隠れニューロン階層
隠れネットワークの数はトレーニングされたニューラルネットワーク処理を基に試算される必要があります。隠れニューロンを計算する簡単な数式はありません。本稿を書く前、私は複数のトライアル&エラー方法を使い、Heaton Research ウェブサイトにアルゴリズムを見つけたわけですが、それが前方選択アルゴリズムを理解する手助けをしてくれました。
Figure 6. 隠れニューロンの数にたいする前方選択アルゴリズム
5.3. アウトプットニューロン階層
ここでの目的のためには。、アウトプットニューロン数は予測に使用するバー数です。隠れニューロンおよびアウトプットニューロンの数が大きいと、ネットワークをトレーニングするのに長い時間がかることを覚えておいてください。本稿では、将来に1本のバーを予測しようとしています。よってアウトプット階層は1個のニューロンでできています。
6. MetaTrader 5 から ENCOGへのトレーニングデータのエクスポート
Encog は ニューラルネットワークのトレーニングにCSV ファイルを採用します。
他のトレーディングソフトウェアから ENCOG にファイルフォーマットがエクスポートされ、トレーディングのために同じファイルフォーマットがを準備するMQL5 スクリプトが実装されるのを見ました。まず1個のインディケータをエクスポートし、続いてのちに複数インディケータのエクスポートをします。
最初のデータ行はコンマ区切りのヘッダです。
DATE,TIME,CLOSE,Indicator_Name1,Indicator_Name2,Indicator_Name3
最初の3つのコラムには日付、時刻、終値が、次のコラムにはインディケータ名が書かれます。トレーニングファイルの次の行にはコンマ区切りデータ、インディケータ値が科学的フォーマットで書かれます。
20110103,0000,0.93377000,-7.8970208860e-002
あるインディケータについての規制スクリプトを下記に示しますから確認ください。
//+------------------------------------------------------------------+ //| ExportToEncog.mq5 | //| Copyright 2011, Investeo.pl | //| http:/Investeo.pl | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, Investeo.pl" #property link "http:/Investeo.pl" #property version "1.00" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ // Export Indicator values for NN training by ENCOG extern string IndExportFileName = "mt5export.csv"; extern int trainSize = 400; extern int maPeriod = 210; MqlRates srcArr[]; double expBullsArr[]; void OnStart() { //--- ArraySetAsSeries(srcArr, true); ArraySetAsSeries(expBullsArr, true); int copied = CopyRates(Symbol(), Period(), 0, trainSize, srcArr); if (copied!=trainSize) { Print("Not enough data for " + Symbol()); return; } int hBullsPower = iBullsPower(Symbol(), Period(), maPeriod); CopyBuffer(hBullsPower, 0, 0, trainSize, expBullsArr); int hFile = FileOpen(IndExportFileName, FILE_CSV | FILE_ANSI | FILE_WRITE | FILE_REWRITE, ",", CP_ACP); FileWriteString(hFile, "DATE,TIME,CLOSE,BullsPower\n"); Print("Exporting indicator data to " + IndExportFileName); for (int i=trainSize-1; i>=0; i--) { string candleDate = TimeToString(srcArr[i].time, TIME_DATE); StringReplace(candleDate,".",""); string candleTime = TimeToString(srcArr[i].time, TIME_MINUTES); StringReplace(candleTime,":",""); FileWrite(hFile, candleDate, candleTime, DoubleToString(srcArr[i].close), DoubleToString(expBullsArr[i], -10)); } FileClose(hFile); Print("Indicator data exported."); } //+------------------------------------------------------------------+
トレーニングで使用する結果ファイルは以下のようなアウトプットです。
DATE,TIME,CLOSE,BullsPower 20110103,0000,0.93377000,-7.8970208860e-002 20110104,0000,0.94780000,-6.4962292188e-002 20110105,0000,0.96571000,-4.7640374727e-002 20110106,0000,0.96527000,-4.4878854587e-002 20110107,0000,0.96697000,-4.6178012364e-002 20110110,0000,0.96772000,-4.2078647318e-002 20110111,0000,0.97359000,-3.6029181466e-002 20110112,0000,0.96645000,-3.8335729509e-002 20110113,0000,0.96416000,-3.7054869514e-002 20110114,0000,0.96320000,-4.4259373120e-002 20110117,0000,0.96503000,-4.4835729773e-002 20110118,0000,0.96340000,-4.6420936126e-002 20110119,0000,0.95585000,-4.6868984125e-002 20110120,0000,0.96723000,-4.2709941621e-002 20110121,0000,0.95810000,-4.1918330800e-002 20110124,0000,0.94873000,-4.7722659418e-002 20110125,0000,0.94230000,-5.7111591557e-002 20110126,0000,0.94282000,-6.2231529077e-002 20110127,0000,0.94603000,-5.9997865295e-002 20110128,0000,0.94165000,-6.0378312069e-002 20110131,0000,0.94414000,-6.2038328069e-002 20110201,0000,0.93531000,-6.0710334438e-002 20110202,0000,0.94034000,-6.1446445012e-002 20110203,0000,0.94586000,-5.2580791504e-002 20110204,0000,0.95496000,-4.5246755566e-002 20110207,0000,0.95730000,-4.4439392954e-002
インディケータStochastic および Williams' Rのオリジナル稿の例に戻りますと、3個のコンマ区切りコラムが必要です。各コラムには個別のインディケータ値が含まれ、よってファイルを拡張し、予備バッファを追加する必要があります。
//+------------------------------------------------------------------+ //| ExportToEncog.mq5 | //| Copyright 2011, Investeo.pl | //| http:/Investeo.pl | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, Investeo.pl" #property link "http:/Investeo.pl" #property version "1.00" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ // Export Indicator values for NN training by ENCOG extern string IndExportFileName = "mt5export.csv"; extern int trainSize = 2000; MqlRates srcArr[]; double StochKArr[], StochDArr[], WilliamsRArr[]; void OnStart() { //--- ArraySetAsSeries(srcArr, true); ArraySetAsSeries(StochKArr, true); ArraySetAsSeries(StochDArr, true); ArraySetAsSeries(WilliamsRArr, true); int copied = CopyRates(Symbol(), Period(), 0, trainSize, srcArr); if (copied!=trainSize) { Print("Not enough data for " + Symbol()); return; } int hStochastic = iStochastic(Symbol(), Period(), 8, 5, 5, MODE_EMA, STO_LOWHIGH); int hWilliamsR = iWPR(Symbol(), Period(), 21); CopyBuffer(hStochastic, 0, 0, trainSize, StochKArr); CopyBuffer(hStochastic, 1, 0, trainSize, StochDArr); CopyBuffer(hWilliamsR, 0, 0, trainSize, WilliamsRArr); int hFile = FileOpen(IndExportFileName, FILE_CSV | FILE_ANSI | FILE_WRITE | FILE_REWRITE, ",", CP_ACP); FileWriteString(hFile, "DATE,TIME,CLOSE,StochK,StochD,WilliamsR\n"); Print("Exporting indicator data to " + IndExportFileName); for (int i=trainSize-1; i>=0; i--) { string candleDate = TimeToString(srcArr[i].time, TIME_DATE); StringReplace(candleDate,".",""); string candleTime = TimeToString(srcArr[i].time, TIME_MINUTES); StringReplace(candleTime,":",""); FileWrite(hFile, candleDate, candleTime, DoubleToString(srcArr[i].close), DoubleToString(StochKArr[i], -10), DoubleToString(StochDArr[i], -10), DoubleToString(WilliamsRArr[i], -10) ); } FileClose(hFile); Print("Indicator data exported."); } //+------------------------------------------------------------------+
結果ファイルにはすべてのインディケータ値が含まれます。
DATE,TIME,CLOSE,StochK,StochD,WilliamsR 20030707,0000,1.37370000,7.1743119266e+001,7.2390220187e+001,-6.2189054726e-001 20030708,0000,1.36870000,7.5140977444e+001,7.3307139273e+001,-1.2500000000e+001 20030709,0000,1.35990000,7.3831775701e+001,7.3482018082e+001,-2.2780373832e+001 20030710,0000,1.36100000,7.1421933086e+001,7.2795323083e+001,-2.1495327103e+001 20030711,0000,1.37600000,7.5398313027e+001,7.3662986398e+001,-3.9719626168e+000 20030714,0000,1.37370000,7.0955352856e+001,7.2760441884e+001,-9.6153846154e+000 20030715,0000,1.38560000,7.4975891996e+001,7.3498925255e+001,-2.3890784983e+000 20030716,0000,1.37530000,7.5354107649e+001,7.4117319386e+001,-2.2322435175e+001 20030717,0000,1.36960000,7.1775345074e+001,7.3336661282e+001,-3.0429594272e+001 20030718,0000,1.36280000,5.8474576271e+001,6.8382632945e+001,-3.9778325123e+001 20030721,0000,1.35400000,4.3498596819e+001,6.0087954237e+001,-5.4946524064e+001 20030722,0000,1.36130000,2.9036761284e+001,4.9737556586e+001,-4.5187165775e+001 20030723,0000,1.34640000,1.6979405034e+001,3.8818172735e+001,-6.5989159892e+001 20030724,0000,1.34680000,1.0634573304e+001,2.9423639592e+001,-7.1555555556e+001 20030725,0000,1.34400000,9.0909090909e+000,2.2646062758e+001,-8.7500000000e+001 20030728,0000,1.34680000,1.2264922322e+001,1.9185682613e+001,-8.2705479452e+001 20030729,0000,1.35250000,1.4960629921e+001,1.7777331716e+001,-7.2945205479e+001 20030730,0000,1.36390000,2.7553336360e+001,2.1035999930e+001,-5.3979238754e+001 20030731,0000,1.36990000,4.3307839388e+001,2.8459946416e+001,-4.3598615917e+001 20030801,0000,1.36460000,5.6996412096e+001,3.7972101643e+001,-5.2768166090e+001 20030804,0000,1.34780000,5.7070193286e+001,4.4338132191e+001,-8.1833910035e+001 20030805,0000,1.34770000,5.3512705531e+001,4.7396323304e+001,-8.2006920415e+001 20030806,0000,1.35350000,4.4481132075e+001,4.6424592894e+001,-7.1972318339e+001 20030807,0000,1.35020000,3.3740028156e+001,4.2196404648e+001,-7.7681660900e+001 20030808,0000,1.35970000,3.0395426394e+001,3.8262745230e+001,-6.1245674740e+001 20030811,0000,1.35780000,3.4155781326e+001,3.6893757262e+001,-6.4532871972e+001 20030812,0000,1.36880000,4.3488943489e+001,3.9092152671e+001,-4.5501730104e+001 20030813,0000,1.36690000,5.1160443996e+001,4.3114916446e+001,-4.8788927336e+001 20030814,0000,1.36980000,6.2467599793e+001,4.9565810895e+001,-2.5629290618e+001 20030815,0000,1.37150000,6.9668246445e+001,5.6266622745e+001,-2.1739130435e+001 20030818,0000,1.38910000,7.9908906883e+001,6.4147384124e+001,-9.2819614711e+000
二番目の例では必要に応じて適切なスクリプトを簡単に作成できます。
7. ニューラルネットワークのトレーニング
ニューラルネットワークのトレーニングはすでに Heaton ResearchによってC#で準備済みです。ENCOG 2.6は 金融時系列予測の基となるEncog.App.Quant 名前空間を実装します。トレーニングスクリプトは柔軟で、あらゆるインプットインディケータの数にたいして簡単に調整できます。ただ DIRECTORY 定数のMetaTrader 5 ディレクトリ位置を変更するだけです。
ネットワークアーキテクチャとトレーニングパラメータは以下の変数を変えることで簡単にカスタマイズできます。
/// <summary> /// The size of the input window. This is the number of bars used to predict the next bar. /// </summary> public const int INPUT_WINDOW = 6; /// <summary> /// The number of bars forward we are trying to predict. This is usually just 1 bar. The future indicator used in step 1 may /// well look more forward into the future. /// </summary> public const int PREDICT_WINDOW = 1; /// <summary> /// The number of bars forward to look for the best result. /// </summary> public const int RESULT_WINDOW = 5; /// <summary> /// The number of neurons in the first hidden layer. /// </summary> public const int HIDDEN1_NEURONS = 12; /// <summary> /// The target error to train to. /// </summary> public const double TARGET_ERROR = 0.01;
コードはそれ自体で説明がよくできています。よってそれを注意して読むことが大切です。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Encog.App.Quant.Normalize; using Encog.Util.CSV; using Encog.App.Quant.Indicators; using Encog.App.Quant.Indicators.Predictive; using Encog.App.Quant.Temporal; using Encog.Neural.NeuralData; using Encog.Neural.Data.Basic; using Encog.Util.Simple; using Encog.Neural.Networks; using Encog.Neural.Networks.Layers; using Encog.Engine.Network.Activation; using Encog.Persist; namespace NetworkTrainer { public class Program { /// <summary> /// The directory that all of the files will be stored in. /// </summary> public const String DIRECTORY = "d:\\mt5\\MQL5\\Files\\"; /// <summary> /// The input file that starts the whole process. This file should be downloaded from NinjaTrader using the EncogStreamWriter object. /// </summary> public const String STEP1_FILENAME = DIRECTORY + "mt5export.csv"; /// <summary> /// We apply a predictive future indicator and generate a second file, with the additional predictive field added. /// </summary> public const String STEP2_FILENAME = DIRECTORY + "step2_future.csv"; /// <summary> /// Next the entire file is normalized and stored into this file. /// </summary> public const String STEP3_FILENAME = DIRECTORY + "step3_norm.csv"; /// <summary> /// The file is time-boxed to create training data. /// </summary> public const String STEP4_FILENAME = DIRECTORY + "step4_train.csv"; /// <summary> /// Finally, the trained neural network is written to this file. /// </summary> public const String STEP5_FILENAME = DIRECTORY + "step5_network.eg"; /// <summary> /// The size of the input window. This is the number of bars used to predict the next bar. /// </summary> public const int INPUT_WINDOW = 6; /// <summary> /// The number of bars forward we are trying to predict. This is usually just 1 bar. The future indicator used in step 1 may /// well look more forward into the future. /// </summary> public const int PREDICT_WINDOW = 1; /// <summary> /// The number of bars forward to look for the best result. /// </summary> public const int RESULT_WINDOW = 5; /// <summary> /// The number of neurons in the first hidden layer. /// </summary> public const int HIDDEN1_NEURONS = 12; /// <summary> /// The target error to train to. /// </summary> public const double TARGET_ERROR = 0.01; static void Main(string[] args) { // Step 1: Create future indicators Console.WriteLine("Step 1: Analyze MT5 Export & Create Future Indicators"); ProcessIndicators ind = new ProcessIndicators(); ind.Analyze(STEP1_FILENAME, true, CSVFormat.DECIMAL_POINT); int externalIndicatorCount = ind.Columns.Count - 3; ind.AddColumn(new BestReturn(RESULT_WINDOW,true)); ind.Process(STEP2_FILENAME); Console.WriteLine("External indicators found: " + externalIndicatorCount); //Console.ReadKey(); // Step 2: Normalize Console.WriteLine("Step 2: Create Future Indicators"); EncogNormalize norm = new EncogNormalize(); norm.Analyze(STEP2_FILENAME, true, CSVFormat.ENGLISH); norm.Stats[0].Action = NormalizationDesired.PassThrough; // Date norm.Stats[1].Action = NormalizationDesired.PassThrough; // Time norm.Stats[2].Action = NormalizationDesired.Normalize; // Close norm.Stats[3].Action = NormalizationDesired.Normalize; // Stoch K norm.Stats[4].Action = NormalizationDesired.Normalize; // Stoch Dd norm.Stats[5].Action = NormalizationDesired.Normalize; // WilliamsR norm.Stats[6].Action = NormalizationDesired.Normalize; // best return [RESULT_WINDOW] norm.Normalize(STEP3_FILENAME); // neuron counts int inputNeurons = INPUT_WINDOW * externalIndicatorCount; int outputNeurons = PREDICT_WINDOW; // Step 3: Time-box Console.WriteLine("Step 3: Timebox"); //Console.ReadKey(); TemporalWindow window = new TemporalWindow(); window.Analyze(STEP3_FILENAME, true, CSVFormat.ENGLISH); window.InputWindow = INPUT_WINDOW; window.PredictWindow = PREDICT_WINDOW; int index = 0; window.Fields[index++].Action = TemporalType.Ignore; // date window.Fields[index++].Action = TemporalType.Ignore; // time window.Fields[index++].Action = TemporalType.Ignore; // close for(int i=0;i<externalIndicatorCount;i++) window.Fields[index++].Action = TemporalType.Input; // external indicators window.Fields[index++].Action = TemporalType.Predict; // PredictBestReturn window.Process(STEP4_FILENAME); // Step 4: Train neural network Console.WriteLine("Step 4: Train"); Console.ReadKey(); INeuralDataSet training = (BasicNeuralDataSet)EncogUtility.LoadCSV2Memory(STEP4_FILENAME, inputNeurons, outputNeurons, true, CSVFormat.ENGLISH); BasicNetwork network = new BasicNetwork(); network.AddLayer(new BasicLayer(new ActivationTANH(), true, inputNeurons)); network.AddLayer(new BasicLayer(new ActivationTANH(), true, HIDDEN1_NEURONS)); network.AddLayer(new BasicLayer(new ActivationLinear(), true, outputNeurons)); network.Structure.FinalizeStructure(); network.Reset(); //EncogUtility.TrainToError(network, training, TARGET_ERROR); EncogUtility.TrainConsole(network, training, 3); // Step 5: Save neural network and stats EncogMemoryCollection encog = new EncogMemoryCollection(); encog.Add("network", network); encog.Add("stat", norm.Stats); encog.Save(STEP5_FILENAME); Console.ReadKey(); } } }
私が一行コメントをつけて関数を EncogUtility.TrainToError() から EncogUtility.TrainConsole()に変えたことにお気づきかもしれません。
EncogUtility.TrainConsole(network, training, 3);
TrainConsole メソッドはネットワークをトレーニングする分数を指定します。例では3分間ネットワークのトレーニングを行います。ネットワークの複雑さとトレーニングデータのサイズに応じて、ネットワークのトレーニングは数分、数時間、数日ということもあります。Heaton Researchウェブサイト上のエラー計算 および トレーニングのアルゴリズム 、また本件に関する別の本をお読みになることをお薦めします。
EncogUtility.TrainToError()メソッドは対象ネットワークエラーが発生したあとトレーニングを停止します。オリジナルの例にあるように望むエラーが起こるまでネットワークのトレー二ングをするため、EncongUtiliy.TrainConsole() にコメントを入れ、EncogUtility.TrainToError() のコメントをなくすとよいでしょう。
EncogUtility.TrainToError(network, training, TARGET_ERROR);
ネットワークのトレーニングが特定エラーまでいかないこともあることに注意が必要です。それは、ニューロン数が少なすぎるせいです。
8. MetaTrader 5 ニューラルインディケータ構築のためのトレーニング済みニューラルネットワーク使用
投資に最良の収益を予測するニューラルネットワークインディケータにによってトレーニング済みネットワークを使用することが可能です。
MetaTrader 5 用ENCOGニューラルインディケータは2つの部分で構成されます。一部は MQL5 で書かれ基本的にネットワークのトレーニングとおなじインディケータをとり、ネットワークにインプットウィンドウインディケータ値を渡します。第二部は C# で書かれインプットデータをタイムボックス化し MQL5にニューラルネットワークのアウトプットを返します。C# インディケータ部は私の前稿『C# コードのMQL5へのエクスポーズ』を基にしています。
using System; using System.Collections.Generic; using System.Text; using RGiesecke.DllExport; using System.Runtime.InteropServices; using Encog.Neural.Networks; using Encog.Persist; using Encog.App.Quant.Normalize; using Encog.Neural.Data; using Encog.Neural.Data.Basic; namespace EncogNeuralIndicatorMT5DLL { public class NeuralNET { private EncogMemoryCollection encog; public BasicNetwork network; public NormalizationStats stats; public NeuralNET(string nnPath) { initializeNN(nnPath); } public void initializeNN(string nnPath) { try { encog = new EncogMemoryCollection(); encog.Load(nnPath); network = (BasicNetwork)encog.Find("network"); stats = (NormalizationStats)encog.Find("stat"); } catch (Exception e) { Console.WriteLine(e.StackTrace); } } }; class UnmanagedExports { static NeuralNET neuralnet; [DllExport("initializeTrainedNN", CallingConvention = CallingConvention.StdCall)] static int initializeTrainedNN([MarshalAs(UnmanagedType.LPWStr)]string nnPath) { neuralnet = new NeuralNET(nnPath); if (neuralnet.network != null) return 0; else return -1; } [DllExport("computeNNIndicator", CallingConvention = CallingConvention.StdCall)] public static int computeNNIndicator([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] double[] t1, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] double[] t2, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] double[] t3, int len, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 5)] double[] result, int rates_total) { INeuralData input = new BasicNeuralData(3 * len); int index = 0; for (int i = 0; i <len; i++) { input[index++] = neuralnet.stats[3].Normalize(t1[i]); input[index++] = neuralnet.stats[4].Normalize(t2[i]); input[index++] = neuralnet.stats[5].Normalize(t3[i]); } INeuralData output = neuralnet.network.Compute(input); double d = output[0]; d = neuralnet.stats[6].DeNormalize(d); result[rates_total-1]=d; return 0; } } }
3つ以外のあらゆるインディケータ数を使いたければ、 必要に応じて computeNNIndicator() メソッドを変更する必要があります。
[DllExport("computeNNIndicator", CallingConvention = CallingConvention.StdCall)] public static int computeNNIndicator([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] double[] t1, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] double[] t2, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] double[] t3, int len, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 5)] double[] result, int rates_total)
今回の倍は、最初の3つの入力パラメータはインディケータインプット値を持つテーブルで、4番目のパラメータはインプットウィンドウの長さです。
SizeParamIndex = 3 インプットウィンドウ長変数を指します。これはインプット変数の数は0から増えていくからです。5番目のパラメータはニューラルネットワークの結果を持つテーブルです。
MQL5 インディケータの部分は C# EncogNNTrainDLL.dll をインポートする必要があり、dllからエクスポートされる関数 initializeTrainedNN() と computeNNIndicator() を使用します。
//+------------------------------------------------------------------+ //| NeuralEncogIndicator.mq5 | //| Copyright 2011, Investeo.pl | //| http:/Investeo.pl | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, Investeo.pl" #property link "http:/Investeo.pl" #property version "1.00" #property indicator_separate_window #property indicator_plots 1 #property indicator_buffers 1 #property indicator_color1 Blue #property indicator_type1 DRAW_LINE #property indicator_style1 STYLE_SOLID #property indicator_width1 2 #import "EncogNNTrainDLL.dll" int initializeTrainedNN(string nnFile); int computeNNIndicator(double& ind1[], double& ind2[],double& ind3[], int size, double& result[], int rates); #import int INPUT_WINDOW = 6; int PREDICT_WINDOW = 1; double ind1Arr[], ind2Arr[], ind3Arr[]; double neuralArr[]; int hStochastic; int hWilliamsR; int hNeuralMA; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping SetIndexBuffer(0, neuralArr, INDICATOR_DATA); PlotIndexSetInteger(0, PLOT_SHIFT, 1); ArrayResize(ind1Arr, INPUT_WINDOW); ArrayResize(ind2Arr, INPUT_WINDOW); ArrayResize(ind3Arr, INPUT_WINDOW); ArrayInitialize(neuralArr, 0.0); ArraySetAsSeries(ind1Arr, true); ArraySetAsSeries(ind2Arr, true); ArraySetAsSeries(ind3Arr, true); ArraySetAsSeries(neuralArr, true); hStochastic = iStochastic(NULL, 0, 8, 5, 5, MODE_EMA, STO_LOWHIGH); hWilliamsR = iWPR(NULL, 0, 21); Print(TerminalInfoString(TERMINAL_DATA_PATH)+"\\MQL5\Files\step5_network.eg"); initializeTrainedNN(TerminalInfoString(TERMINAL_DATA_PATH)+"\\MQL5\Files\step5_network.eg"); //--- return(0); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ 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 calc_limit; if(prev_calculated==0) // First execution of the OnCalculate() function after the indicator start calc_limit=rates_total-34; else calc_limit=rates_total-prev_calculated; ArrayResize(neuralArr, rates_total); for (int i=0; i<calc_limit; i++) { CopyBuffer(hStochastic, 0, i, INPUT_WINDOW, ind1Arr); CopyBuffer(hStochastic, 1, i, INPUT_WINDOW, ind2Arr); CopyBuffer(hWilliamsR, 0, i, INPUT_WINDOW, ind3Arr); computeNNIndicator(ind1Arr, ind2Arr, ind3Arr, INPUT_WINDOW, neuralArr, rates_total-i); } //Print("neuralArr[0] = " + neuralArr[0]); //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
USDCHF について日次データ、インディケータストキャスティック および ウィリアムズの %R でトレーニングされたインディケータのアウトプットを確認します。
図7 ニューラルEncogインディケータ
インディケータは、次のバーへの投資に最良予測を示します。
将来のあるバーのインディケータを移動させたことにお気づきかもしれません。
PlotIndexSetInteger(0, PLOT_SHIFT, 1);
これはインディケータが予測的なインディケータであることを指しています。ここではニューラルインディケータを作成したので、そのインディケータを基にExpert Advisorを構築する準備ができています。
9. ニューラルインディケータを基にしたExpert Advisor
Expert Advisor はニューラルインディケータのアウトプットを取り、為替の売り買いを判断します。私の第一印象は、インディケータがゼロより上であれば買い、ゼロより下であれば売りで、それが意味するところは、既定の時間ウィンドウにおいてベストの戻り予測がプラスならば買い、ベストな戻り予測がマイナスならば売り、です。
初期検証をいくらかしたあと、パフォーマンスはもっとよくなるかもしれないと判明しました。よって、私は変数「強い情報トレンド」と「強い下降トレント」を採用しました。それは有名な「トレンドはフレンド」ルールによると、強いトレンドにいるばあい、トレードを終了する理由はないということです。
また、 ATR に関するHeaton Researchのフォーラムで、ストップロスを移動するために、 MQL5 forumフォーラムで見つけたシャンデリ ATRインディケータを使うようアドバイスを得ました。それは実際バックテストの間に資本ゲインを増やします。下にExpert Advisor のソースコードを貼りつけます。
//+------------------------------------------------------------------+ //| NeuralEncogAdvisor.mq5 | //| Copyright 2011, Investeo.pl | //| http:/Investeo.pl | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, Investeo.pl" #property link "http:/Investeo.pl" #property version "1.00" double neuralArr[]; double trend; double Lots=0.3; int INPUT_WINDOW=8; int hNeural,hChandelier; //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- ArrayResize(neuralArr,INPUT_WINDOW); ArraySetAsSeries(neuralArr,true); ArrayInitialize(neuralArr,0.0); hNeural=iCustom(Symbol(),Period(),"NeuralEncogIndicator"); Print("hNeural = ",hNeural," error = ",GetLastError()); if(hNeural<0) { Print("The creation of ENCOG indicator has failed: Runtime error =",GetLastError()); //--- forced program termination return(-1); } else Print("ENCOG indicator initialized"); hChandelier=iCustom(Symbol(),Period(),"Chandelier"); Print("hChandelier = ",hChandelier," error = ",GetLastError()); if(hChandelier<0) { Print("The creation of Chandelier indicator has failed: Runtime error =",GetLastError()); //--- forced program termination return(-1); } else Print("Chandelier indicator initialized"); //--- return(0); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- long tickCnt[1]; int ticks=CopyTickVolume(Symbol(),0,0,1,tickCnt); if(tickCnt[0]==1) { if(!CopyBuffer(hNeural,0,0,INPUT_WINDOW,neuralArr)) { Print("Copy1 error"); return; } // Print("neuralArr[0] = "+neuralArr[0]+"neuralArr[1] = "+neuralArr[1]+"neuralArr[2] = "+neuralArr[2]); trend=0; if(neuralArr[0]<0 && neuralArr[1]>0) trend=-1; if(neuralArr[0]>0 && neuralArr[1]<0) trend=1; Trade(); } } //+------------------------------------------------------------------+ //| Tester function | //+------------------------------------------------------------------+ double OnTester() { //--- //--- return(0.0); } //+------------------------------------------------------------------+ void Trade() { double bufChandelierUP[2]; double bufChandelierDN[2]; double bufMA[2]; ArraySetAsSeries(bufChandelierUP,true); ArraySetAsSeries(bufChandelierUP,true); ArraySetAsSeries(bufMA,true); CopyBuffer(hChandelier,0,0,2,bufChandelierUP); CopyBuffer(hChandelier,1,0,2,bufChandelierDN); MqlRates rates[]; ArraySetAsSeries(rates,true); int copied=CopyRates(Symbol(),PERIOD_CURRENT,0,3,rates); bool strong_uptrend=neuralArr[0]>0 && neuralArr[1]>0 && neuralArr[2]>0 && neuralArr[3]>0 && neuralArr[4]>0 && neuralArr[5]>0 && neuralArr[6]>0 && neuralArr[7]>0; bool strong_downtrend=neuralArr[0]<0 && neuralArr[1]<0 && neuralArr[2]<0 && neuralArr[3]<0 && neuralArr[4]<0 && neuralArr[5]<0 && neuralArr[6]<0 && neuralArr[7]<0; if(PositionSelect(_Symbol)) { long type=PositionGetInteger(POSITION_TYPE); bool close=false; if((type==POSITION_TYPE_BUY) && (trend==-1)) if(!(strong_uptrend) || (bufChandelierUP[0]==EMPTY_VALUE)) close=true; if((type==POSITION_TYPE_SELL) && (trend==1)) if(!(strong_downtrend) || (bufChandelierDN[0]==EMPTY_VALUE)) close=true; if(close) { CTrade trade; trade.PositionClose(_Symbol); } else // adjust s/l { CTrade trade; if(copied>0) { if(type==POSITION_TYPE_BUY) { if(bufChandelierUP[0]!=EMPTY_VALUE) trade.PositionModify(Symbol(),bufChandelierUP[0],0.0); } if(type==POSITION_TYPE_SELL) { if(bufChandelierDN[0]!=EMPTY_VALUE) trade.PositionModify(Symbol(),bufChandelierDN[0],0.0); } } } } if((trend!=0) && (!PositionSelect(_Symbol))) { CTrade trade; MqlTick tick; MqlRates rates[]; ArraySetAsSeries(rates,true); int copied=CopyRates(Symbol(),PERIOD_CURRENT,0,INPUT_WINDOW,rates); if(copied>0) { if(SymbolInfoTick(_Symbol,tick)==true) { if(trend>0) { trade.Buy(Lots,_Symbol,tick.ask); Print("Buy at "+tick.ask+" trend = "+trend+" neuralArr = "+neuralArr[0]); } if(trend<0) { trade.Sell(Lots,_Symbol,tick.bid); Print("Sell at "+tick.ask+" trend = "+trend+" neuralArr = "+neuralArr[0]); } } } } } //+------------------------------------------------------------------+
Expert Advisor USDCHF 通貨の D1 データについて実行されました。データの約50%はトレーニングサンプルから取られています。
10. Expert Advisor バックテスト結果
下にバックテスト結果を貼りつけます。バックテストは 2000.01.01 ~2011.03.26で実行されました。
図8 ニューラルExpert Advisor バックテスト結果
図9 ニューラルExpert Advisor 残高/資本バックテスト結果
このパフォーマンスは他の時間枠や他の商品に対して行った場合は全くことなることに注意ください。
この EA は学習用として取扱い、今後の調査のスタート地点としてください。個人的見解は、ネットワークはすべての特定期間をより堅牢にするため保持可能だというものです。すでにそれを行う方法を見つけた方もいるかもしません。おそらく、ニューラルインディケータを基にして売り/買いの予測を作成するもっとよい方法があるでしょう。読者のみなさんにいろいろ試してみることをお薦めします。
おわりに
以下の記事では、私はニューラル予測インディケータ、ENCOGマシーン学習のフレームワークを使用し、そのインディケータを基にexpert advisor を構築する方法を提供しました。ソースコード、コンパイルされたバイナリ、 DLL、トレーニングされたネットワークはすべて添付があります。
「.NETでのDLL の二重ラップ」のためにファイルCloo.dll、 encog-core-cs.dll、log4net.dll はクライアント端末のフォルダに配置します。
The EncogNNTrainDLL.dllファイルは \Terminal Data folder\MQL5\Libraries\ フォルダに配置します。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/252





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