ラベル MQL超入門 の投稿を表示しています。 すべての投稿を表示
ラベル MQL超入門 の投稿を表示しています。 すべての投稿を表示

2016年6月10日金曜日

[MQL超入門] その025 プロファイリングを使った実行速度調査&クラス呼び出し速度改善

すこし超入門からは外れるかもしれませんが、速度改善のお話しです。

インジケータを作っていて、あー遅いなー?と思うことがあります。
どこで遅くなっているのか調査する場合、ソースコードを眺めて追いかける良いツールがメタエディタにはついています。

プロファイリング機能です。

これは、プログラムを実行した際、関数毎に時間を計測してくれる機能です。これを使うと、どこで遅くなっているのかが見た目で編別可能になります。

早速使ってみましょう。
遅いなーと思うプログラムをメタエディタで開きます。
ツールバーのプロファイルスタートボタンを押します。下画像の赤丸の箇所です。

■メタエディタ プロファイル開始ボタン

計測が開始されると、赤い■ボタンに変わりますのでチャートにインジケータが表示された後に、この赤■ボタンを押します。

■メタエディタ プロファイル終了ボタン

■メタエディタ プロファイル終了したあとの表示

計測が終了すると、メタエディタ上に実行にかかった時間が表示されます。ソースコード右端にもバーで表示されます。
このバーが大きいほど時間がかかっている個所になりますので、改善方法がないかどうかを調査していく形になります。

MQLにおける速度改善の基本は

・ループの中で余計な処理をしない。
・何度もループしない。

の2点にほぼ集約されます。特にループ外へ処理を追い出す修正は効果が高いため、具体例を挙げたいと思います。

■ダメな例
for(int i = 0; i < rates_total; i++)
{
    double currentClose = iClose(NULL,0, 0);
    if( currentClose > iClose(NULL, 0, i) )
    {
        //何か処理
    }
}

上記のプログラムでは、iClose(NULL, 0, 0)の結果はループ変数のiに全く影響をうけません。このような処理はループの外に追い出す必要があります。

■修正例
double currentClose = iClose(NULL,0, 0);
for(int i = 0; i < rates_total; i++)
{
    if( currentClose > iClose(NULL, 0, i) )
    {
        //何か処理
    }
}

この修正で、処理時間が約1/2になります。

修正前処理時間 = iClose処理時間 * 2 * rates_totals

修正後処理時間 = iClose処理時間 + iClose処理時間 * rates_totals

実際プロファイリング機能で見てみましょう。

■ループ内の処理をループ外に追い出した場合の速度差

InnnerCallに対してBeforeCallは処理時間を示すバーの幅が半分になっていることが見て取れるかと思います。
このようにループの中の処理見直しは改善効果が高いため、もし表示が遅いなーということがあれば、ループ内処理を中心に見直すことになります。


プロファイリングといえば、以前MQLのクラスメソッドの呼び出しが異常に遅い!と文句たらたらだったことがあります。
[MT4プログラミング]クラスメソッドの呼び出しにご用心

そろそろ治ってないかなーとおもい、プロファイリングの記事を書くついでに、MT4 Build971にて再計測してみました。使用したプログラムは次の通りです。

//------------------------------------------------------------------
// 速度テスト
#property copyright "Copyright 2016,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict
#property indicator_chart_window

#define MAXCOUNT 10000

class COpen
{
public:
   void IndirectionOpen()
   {
         iOpen(NULL, 0, 1);
   }
};

COpen m_open;

COpen *m_pOpen;


//------------------------------------------------------------------
// 初期化
int OnInit()
{
   m_pOpen = GetPointer(m_open);
   DirectCall();
   IndirectionCall();
   ClassCall();
   ClassPointerCall();

   return(INIT_SUCCEEDED);
}

//------------------------------------------------------------------
// 直接呼出し
void DirectCall()
{
   for(int i = 0; i < MAXCOUNT; i++)
   {
      iOpen(NULL, 0, 1);
   }
}

//------------------------------------------------------------------
// 間接呼出し
void IndirectionCall()
{
   for(int i = 0; i < MAXCOUNT; i++)
   {
      IndirectionOpen();
   }
}

void IndirectionOpen()
{
      iOpen(NULL, 0, 1);
}

//------------------------------------------------------------------
// クラス呼び出し
void ClassCall()
{
   for(int i = 0; i < MAXCOUNT; i++)
   {
      m_open.IndirectionOpen();
   }
}

//------------------------------------------------------------------
// クラス呼び出し
void ClassPointerCall()
{
   for(int i = 0; i < MAXCOUNT; i++)
   {
      m_pOpen.IndirectionOpen();
   }
}


//------------------------------------------------------------------
// 計算処理
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[])
  {
//---
   
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+


OnInitの中で、次の4つの関数を呼び出して速度比較してみます。
・直接iOpenをコール(DirectCall)
・一回関数をはさむ(IndirectionCall)
・クラスの直接参照(ClassCall)
・クラスのポインタ参照(ClassPointerCall)

結果は次の形になりました。
MQL プロファイラレポート - SpeedTest.mq4
関数 カウント 時間 パーセント グラフ
OnInit 27 1 2 422 99.71%
COpen::IndirectionOpen 20000 730 30.05%
IndirectionCall 31 1 686 28.24%
ClassCall 32 1 686 28.24%
ClassPointerCall 33 1 686 28.24%
iOpen 40000 419 17.25%
IndirectionOpen 54 10000 365 15.03%
DirectCall 30 1 363 14.94%
@global_initializations 1 6 0.25%
OnCalculate 86 26 1 0.04%
COpen::~COpen 1 0 0.00%
COpen::COpen 20 1 0 0.00%
@global_deinitializations 1 0 0.00%
GetPointer 29 1 0 0.00%
Copyright 2001-2013, MetaQuotes Software Corp.



直接参照が一番早いのは当然として、関数呼び出し~クラス呼び出しについては差がなくなっています。以前はクラス呼び出しが異常なほど(それこそ10倍ぐらい)遅かったのですが改善したようです。

これでもっと積極的にクラスが利用できます。

「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ MetaTraderへ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。

2016年5月17日火曜日

[MQL超入門] その024 バックテストを活用しよう。その3 パラメータの最適化2

さっそくパラメータの最適化を行った結果を見てみましょう。

最適化を行うと、通常のバックテストを行った時とは違う[最適化結果]と[最適化グラフ]のタブがストラテジーテスターに表示されます。

まずは[最適化結果]タブを見てみます。
・図024.01 最適化結果
[損益]が大きい順に並んでいて、[パラメーターの入力]を見るとパラメータがそれぞれどの値をとった結果なのかが分かります。
行をダブルクリックするか、行を右クリックして[パラメータの設定(I)]をクリックすると、その行のパラメータを入力パラメータとして適用することができます。
・図024.02 パラメータの設定

ここで1つ注意点があります。
[最適化結果]タブの中で右クリックすると[マイナスの結果を表示しない(U)]という項目があるので、ここをクリックしてチェックを外しておきましょう。
・図024.03 マイナスの結果を表示しない
デフォルトではこれにチェックが入っていますが、そのままだと損益が0以上になるパラメータの組み合わせしか最適化の結果として表示されなくなります。
最終的には損益がマイナスのEAなんて使わないとは思いますが、たとえマイナスでもどんなパラメータの組み合わせが効率がいいかは確認したいと思いますので、マイナスの結果も表示しておくのがいいと思います。

では次に[最適化グラフ]タブを見てみます。
・図024.04 最適化グラフ
こちらはパラメータの組み合わせパターンを横軸、残高を縦軸としたグラフになっています。
グラフ上の点にマウスのカーソルを当てると、残高とパラメータの組み合わせが表示されます。

ただ、どの組み合わせが一番良い結果かは[最適化結果]タブでも見れますし、並び替えもされるのでそちらのほうが分かりやすいと思います。
これだけでは[最適化グラフ]はあまり意味がなさそうに思えるかもしれませんが、[最適化グラフ]にはもう1つ、[2Dサーフェス]という機能があります。

グラフ内を右クリックして、[2Dサーフェス(2)]をクリックします。
・図024.05 2Dサーフェスの表示
すると、下図のように2つのパラメータを横軸と縦軸にして、結果の良さを色の濃さで表してくれます。色が濃いほうが良い結果になっています。
・図024.06 2Dサーフェス
この例では、横軸が[移動平均の期間]、縦軸が[移動平均線の種類]になっています。
上から2行目と4行目が濃くなっているように見えますので、[移動平均線の種類]は単純移動平均線か平滑移動平均線が良さそうです。また、全体的に右側のほうが色が濃いようですので、[移動平均の期間]は、この範囲であれば長めのほうがよさそうです。
このように、2つのパラメータを2次元の表にして結果を確認できることで、効率のいいパラメータの傾向を見ることができます。

このようにしてパラメータの最適化を行っていきますが、前にも書いたとおり、過剰な最適化(カーブフィッティング)には注意が必要です。
例えば1番良い結果が、2Dサーフェスを見てポツンと1つだけ浮いているような場合、ある条件下でたまたま、という場合もあります。

さて、これでEAの作成から検証まで、一通りができるようになりました。
以前にも書きましたが、プログラムは「習うより慣れよ」が基本です。
頭の中でずっと考えるだけでなく、思いついたらとにかく手を動かして、やってみて結果を見てまた考える、というのも習得の近道です。どんどんチャレンジしましょう!

次の回へ

「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ MetaTraderへ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。

2016年5月13日金曜日

[MQL超入門] その023 バックテストを活用しよう。その2 パラメータの最適化1

EAの良し悪しは、プログラムのロジックだけでは決まりません。同じロジックでも、パラメータが変わることで全く違う成績にもなります。

EAのパラメータとは、例えば前に作成した移動平均線のEAであれば、移動平均の期間や、移動平均の種類などを指します。

その時の記事はこちら
[MQL超入門] その021 本格的にEAを作ってみよう。その2 カスタム指標を使用する

とはいえパラメータを少しずつ変えてバックテストを実行して検証するのは大変なんですが、MT4には最適化という便利な機能があります。最適化とは、指定したパラメータの組み合わせを自動的に実行し、結果を比較することができる機能です。
今回は、最適化の手順を説明します。

ストラテジーテスターの[セッティング]タブの[最適化]にチェックを付けます。
・図023.01 最適化の選択

[エキスパート設定]ボタンをクリックします。
・図023.02 エキスパート設定の表示

EAの設定画面が表示されます。
まず[テスト設定]タブの設定を行います。
・図023.03 エキスパート設定 テスト設定
[最適化パラメータ]では、パラメータの良し悪しを何の数値で評価するかを選択します。
基本的には[Balance]でよいと思いますが、以下から選択することが出来ます。
 ・Balance (残高)
 ・Profit Factor (プロフィットファクタ)
 ・Expected Payoff (期待利得)
 ・Maximal Drawdown (最大ドローダウン)
 ・Drawdown percent (相対ドローダウン)
 ・Custom (EAに埋め込まれた評価値算出処理の出力する数値)

[遺伝的アルゴリズム]も、基本的にチェックでよいです。
遺伝的アルゴリズムは、ランダムなパラメータの組み合わせから評価の良いものを抽出し、それを何度も繰り返すことで、高い確率で最適な組み合わせに行き着くと言われています。
遺伝的アルゴリズムのチェックを外した場合、パラメータの組み合わせを総当りで実行します。組み合わせが少ない場合は総当りの方が確実ですが、組み合わせが多い場合には遺伝的アルゴリズムを使用することで短時間で最適化が行えます。

必要に応じて、[初期証拠金]を増やすこともできます。
また、[ポジション]でロング(買)とショート(売)どちらかしか持たないこともできます。
ただ基本的にこれらは変更しなくてもよいかと思います。

次に[パラメーターの入力]タブで、最適化したいパラメータを選択します。
・図023.04 エキスパート設定 パラメーターの入力
最適化するパラメータを、左のチェックを入れて選択します。最適化するパラメータの数が増えるとそれだけ時間がかかりますので、2~3個までに留めておくのがよいと思います。
今回は[移動平均の期間]と[移動平均線の種類]を最適化します。
その右の設定値は、[スタート]の値から[ストップ]の値までを[ステップ]の値刻みで変化させて最適化を行うという意味になります。また、チェックを入れたパラメータは[値]の値は無視されます。

[最適化]タブでは、EAを停止するための制限を設けることができますが、下手に制限をかけるとEAの成績が正しく評価できないこともありますので、基本的には何もせず無制限としておきます。

これらの設定を行ってから、ストラテジーテスターの[スタート]ボタンをクリックすると、最適化が開始されます。
時間がかかると思いますが、通常のバックテストと同様で、緑のバーが右端まで到達し、[ストップ]が[スタート]に戻ったら完了です。

では最適化の結果を確認したいと思いますが、長くなりましたので続きは次回に。

次の回へ

「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ MetaTraderへ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。

2016年5月11日水曜日

[MQL超入門] その021 本格的にEAを作ってみよう。その2 カスタム指標を使用する

それでは、独自に作成したインジケータ(カスタム指標)を使用したEAを作成しましょう。

インジケータは標準で入っている「Custom Moving Averages」を使用します。
移動平均線のインジケータなので、戦略や結果は前回とほとんど変わりませんが、これを使って独自のインジケータの使い方を説明します。

それではコードを見てみましょう。
今回は[EA_MACustom]という名前で新しいEAを作成しています。
//+------------------------------------------------------------------+
//|                                                  EA_MACustom.mq4 |
//|                        Copyright 2016, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict

#include <Custom/TradingWrapper.mqh>

input int MagicNumber = 37858262; //マジックナンバー 他のEAと当らない値を使用する。
input int MaxPosition = 1;        //最大ポジション数
input double SpreadFilter = 2;    //最大スプレット値(PIPS)

input double Lot = 0.02;          //売買ロット
input uint Slippage = 2;          //許容スリップページ(pips)
input uint StopLoss = 200;        //ストップロス
input uint TakeProfit = 200;      //利益確定

input int Period = 25;                    //移動平均の期間
input int Shift = 0;                      //移動平均線の表示を右シフトする本数
input ENUM_MA_METHOD Method = MODE_SMA;   //移動平均線の種類

const string IndicatorName = "Custom Moving Averages";

// トレード補助クラス
CTradingWrapper m_wrapper(Symbol(), MagicNumber, MaxPosition, SpreadFilter);

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
//---
   
//---
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
   
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
//---
   string symbol = Symbol();
   static ENUM_TIMEFRAMES target = PERIOD_H1;
   static datetime before = 0;

   // 1時間の確定足でのみ動作
   datetime current = iTime(symbol, target, 0);
   if( before == current )
   {
      return ;
   }
   before = current;

   m_wrapper.RefreshPositions();

   // 1つ前と2つ前の足の終値を取得する
   double close1 = iClose( symbol, target, 1 );
   double close2 = iClose( symbol, target, 2 );
   
   // 1つ前と2つ前の移動平均の値を取得する
   double ma1 = iCustom(symbol, target, IndicatorName, Period, Shift, Method, 0, 1 );
   double ma2 = iCustom(symbol, target, IndicatorName, Period, Shift, Method, 0, 2 );
   
   int orderCmd = OP_NONE;
   if ( close1 > ma1 && close2 < ma2 )
   {
      // 終値が移動平均線を上抜けたら買う
      orderCmd = OP_BUY;
   }
   else if ( close1 < ma1 && close2 > ma2 )
   {
      // 終値が移動平均線を下抜けたら売る
      orderCmd = OP_SELL;
   }
   
   if ( orderCmd != OP_NONE )
   {
      bool hasPosition = ( m_wrapper.GetPositionCount() > 0 );
      if ( hasPosition )
      {
         // ポジションを保持していれば決済する
         m_wrapper.CloseOrder( 0, Slippage );
      }

      m_wrapper.RefreshPositions();
      
      // 注文する
      m_wrapper.SendOrder( orderCmd, Lot, 0, Slippage, StopLoss, TakeProfit );
   }   
}
//+------------------------------------------------------------------+
見れば分かると思いますが、前回とほとんど同じですね。
前回との違いだけ説明していきます。

input int Period = 25;                    //移動平均の期間
input int Shift = 0;                      //移動平均線の表示を右シフトする本数
input ENUM_MA_METHOD Method = MODE_SMA;   //移動平均線の種類
移動平均線の条件を入力パラメータとして宣言しています。
これらの条件は「Custom Moving Averages」インジケータのパラメータです。
基本的にインジケータのパラメータはEAの入力パラメータとしておくとよいです。

const string IndicatorName = "Custom Moving Averages";
使用するインジケータの名前です。
[Indicators]フォルダに格納されている名前どおりに指定します。この場合は[Indicators]フォルダ直下にあるインジケータを使用しているので名前のみですが、子フォルダの下にあるインジケータの場合はフォルダ階層も含めて指定します。
例えば、このインジケータが[Indicators]フォルダの下の[Custom]フォルダに格納されている場合は、"Custom/Custom Moving Averages"のように指定します。

   // 1つ前と2つ前の移動平均の値を取得する
   double ma1 = iCustom(symbol, target, IndicatorName, Period, Shift, Method, 0, 1 );
   double ma2 = iCustom(symbol, target, IndicatorName, Period, Shift, Method, 0, 2 );
ここでカスタムインジケータの値を取得しています。
前回のiMA()関数ではなく、カスタムインジケータの値を取得するiCustom()関数を使用しています。
iCustom()関数の引数は以下の通りです。
・string symbol
  通過ペア名。
・int timeframe
  時間軸。時間軸は、以下のように定義されています。
  ENUM_TIMEFRAMES列挙型
・string name
  使用するインジケータの名前。
・(インジケータのパラメータ)
  インジケータのパラメータ。(数は可変)
  パラメータの数だけ、順番に指定します。
・int mode
  インジケータの中の取得したいライン(バッファ)の番号。(0~)
  インジケータによっては、1つのインジケータの中に複数のラインを表示するものがあります。
  その中のどのラインの値を取得するかを、0から始まるライン番号で指定します。
・int shift
  インジケータの値を取得する足の位置。(0~)

引数の中のインジケータのパラメータは、インジケータをチャートに表示した時の[パラメーターの入力]タブの通りの内容と順番になります。
・図021.01 インジケータ パラメーターの入力
また、コードでインジケータのパラメータを確認したい場合、ナビゲーターの[インディケータ]の中のインジケータを右クリックし、[修正]をクリックします。
・図021.02 インジケータのコード表示
メタエディターにてインジケータのコードが表示されますので、入力パラメータの宣言を確認できます。
・図021.03 インジケータのコード確認

後ろから2番目のmode引数のインジケータのライン番号は、インジケータをチャートに表示した時の[色の設定]タブで確認できます。
ラインの一覧が表示されますので、一番左の#の列がラインの番号になります。
・図021.04 インジケータ 色の設定
コードでインジケータのラインを確認したい場合は、パラメータの場合と同様にメタエディターでインジケータのコードを開きます。
SetIndexBuffer()関数でライン番号とラインの元になるデータを指定していますので、こちらで確認することができます。

このように、iCustom()関数を使用することによって、独自のインジケータの値を取得することができます。

ここまでで、簡単にですが一通りEAを作成することが出来るようになったかと思います。これらを参考に、ぜひ自作のEAを作ってみてください。
本ブログでも公開している無料インジケータもありますので、よければそちらなども使用して試してみてください。

次の回へ

「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ MetaTraderへ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。

2016年5月10日火曜日

[MQL超入門] その022 バックテストを活用しよう。その1 レポートの見方

EAを作ったら、どんどんバックテストして確認していきましょう。
バックテスト結果の詳細はストラテジーテスターの[レポート]タブで確認できるので、この見方を理解して、望ましい結果を得れるようなEAを作っていきましょう。

・図022.01 ストラテジーテスター レポート
それぞれの項目の意味を、一通り説明します。

・テストバー数
  バックテストで使用したバー(足)の数
・モデルティック数
  バックテストで使用したティック数。
・モデリング品質
  バックテストで使用したティックの割合。
・不整合チャートエラー
  エラーのあったバーの数。
・初期証拠金
  用意する初期資金。
・スプレッド
  バックテストに適用するスプレッド(0.1pips単位)。

ここまではあまり重要ではありません。ここから下が、EAの成績として確認すべき内容です。
その中で、特に重要な項目を赤色にしてあります。

・純益
  総利益 - 総損失。
  トータルの利益です。
・総利益
  勝ちトレードの合計金額。
・総損失
  負けトレードの合計金額。
・プロフィットファクタ
  総利益 ÷ 総損失。
・期待利得
  総損益 ÷ 総取引数。
  取引1回あたりどれくらいの利益が期待できるかということになります。
・絶対ドローダウン
  初期資金がいくら減ったか。
・最大ドローダウン
  テスト期間において最大の損失額。
・相対ドローダウン
  資金が最大何パーセント減ったか。
・総取引数
  テスト期間において取引した回数。
・売りポジション(勝率%)
  売りトレードの回数と勝率。
・買いポジション(勝率%)
  買いトレードの回数と勝率。
・勝率(%)
  全トレードに対する勝ちトレードの割合。
・負率(%)
  全トレードに対する負けトレードの割合。
・最大勝トレード
  1回のトレードで勝った最大額。
・最大敗トレード
  1回のトレードで負けた最大額。
・平均勝トレード
  勝ちトレードで勝った金額の平均。
・平均敗トレード
  負けトレードで負けた金額の平均。
・最大連勝(金額)
  連勝の最大回数と、その時の利益。
・最大連敗(金額)
  連敗の最大回数と、その時の損失。
・最大連勝(トレード数)
  連勝の最大利益と、その時の連勝数。
・最大連敗(トレード数)
  連敗の最大損失と、その時の連敗数。
・平均連勝
  連勝回数の平均。
・平均連敗
  連敗回数の平均。

赤色で書いた、特に重要な項目について少し補足します。

■純益
このEAがどれだけ利益を出すのかになるので、まず確認すべき項目です。もちろん多いほうが良いです。
バックテストでプラスにならないEAが使われることはまずないと思います。

■プロフィットファクタ
リスクに対してリターンがどれだけ大きいかを表します。1未満だと損失が出ていて、1より大きいと利益が出ていることになります。
これも大きいほうが良いのですが、バックテストでのプロフィットファクタは大きすぎると逆に注意が必要です。なぜなら、プロフィットファクタが大きすぎることは、カーブフィッティングが疑われるからです。
カーブフィッティングとは、バックテスト期間の相場にぴったり合うように、パラメータ等を過剰に最適化することです。過去の相場はあくまで過去のものなので、未来の相場には当てはまらず役に立たない、逆にマイナスになる、という場合も多々あります。
それを理解して、あくまで参考程度に留めましょう。一般的に1.5~2.0くらいが優秀と言われます。

■最大ドローダウン
連敗も含めて、実際に連続で失った金額で、最も負けた時の負け幅です。
これだけのリスクは覚悟する必要があるという意味になるので、これらを元に用意すべき資金などを判断するのに使用します。

■総取引数
これ自体は成績には直接関係ありませんが、この数字が小さいと各指標の信頼性が下がります。
なので最低でも100以上が望ましいです。

■勝率 / 平均勝トレード / 平均敗トレード
勝率だけではあまり重要ではありません。
勝率の数字は1回の勝ち/負けの金額に関わらないので、例えば9回負けて1回勝っても(勝率10%でも)その1回でトータルがプラスになることもありますし、その逆の勝率90%でも1回の負けでトータルがマイナスになることもあるためです。
これはEAの特徴によりますのでどちらが良いというものではありませんが、このように勝率に加えて、平均の勝ち/負け額とのバランスで判断することが重要になります。


また、レポートは保存することができます。
[レポート]タブの中で右クリックし、[レポートの保存(S)]をクリックすると、保存先を指定して任意の場所に保存できます。
・図022.02 レポートの保存
レポートはグラフの画像と共にHTML形式で保存されるので、後から見るのにも便利です。
・図022.03 保存されたレポート

次の回へ

「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ MetaTraderへ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。

2016年5月9日月曜日

[MQL超入門] その020 本格的にEAを作ってみよう。その1 テクニカル指標を使用する

いよいよテクニカル指標を使って本格的にEAを作ってみましょう。
本格的に、と言ってもまずはシンプルに、移動平均線に対して終値が上抜ければ買い、下抜ければ売る、という戦略にします。1ポジションのみで両建てなし、いわゆるドテン注文です。
とりあえず時間軸を1時間、25本での単純移動平均として作りましょう。

始めに断っておきますが、この戦略が有効だという意味では全くありません。
あくまでテクニカル指標を使ったEAを作る例としているだけです。

それでは前に作った簡単EAと同様に、[テスト]フォルダの下に[EA_MA]という名前のEAを新たに作成しましょう。

新しいEAの作成方法はこちら
[MQL超入門] その014 とりあえずやってみよう!簡単EA作成1

そして、次の変更を加えます。
//+------------------------------------------------------------------+
//|                                                        EA_MA.mq4 |
//|                        Copyright 2016, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict

#include <Custom/TradingWrapper.mqh>

input int MagicNumber = 37858261; //マジックナンバー 他のEAと当らない値を使用する。
input int MaxPosition = 1;        //最大ポジション数
input double SpreadFilter = 2;    //最大スプレット値(PIPS)

input double Lot = 0.02;          //売買ロット
input uint Slippage = 2;          //許容スリップページ(pips)
input uint StopLoss = 200;        //ストップロス
input uint TakeProfit = 200;      //利益確定

input int MaPeriod = 25;                              //移動平均の期間
input ENUM_MA_METHOD MaMethod = MODE_SMA;             //移動平均線の種類
input ENUM_APPLIED_PRICE AppliedPrice = PRICE_CLOSE;  //移動平均を計算する価格の種類

// トレード補助クラス
CTradingWrapper m_wrapper(Symbol(), MagicNumber, MaxPosition, SpreadFilter);

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
//---
   
//---
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
   
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
//---
   string symbol = Symbol();
   static ENUM_TIMEFRAMES target = PERIOD_H1;
   static datetime before = 0;

   // 1時間の確定足でのみ動作
   datetime current = iTime(symbol, target, 0);
   if( before == current )
   {
      return ;
   }
   before = current;

   m_wrapper.RefreshPositions();

   // 1つ前と2つ前の足の終値を取得する
   double close1 = iClose( symbol, target, 1 );
   double close2 = iClose( symbol, target, 2 );
   
   // 1つ前と2つ前の移動平均の値を取得する
   double ma1 = iMA( symbol, target, MaPeriod, 0, MaMethod, AppliedPrice, 1 );
   double ma2 = iMA( symbol, target, MaPeriod, 0, MaMethod, AppliedPrice, 2 );
   
   int orderCmd = OP_NONE;
   if ( close1 > ma1 && close2 < ma2 )
   {
      // 終値が移動平均線を上抜けたら買う
      orderCmd = OP_BUY;
   }
   else if ( close1 < ma1 && close2 > ma2 )
   {
      // 終値が移動平均線を下抜けたら売る
      orderCmd = OP_SELL;
   }
   
   if ( orderCmd != OP_NONE )
   {
      bool hasPosition = ( m_wrapper.GetPositionCount() > 0 );
      if ( hasPosition )
      {
         // ポジションを保持していれば決済する
         m_wrapper.CloseOrder( 0, Slippage );
      }

      m_wrapper.RefreshPositions();
      
      // 注文する
      m_wrapper.SendOrder( orderCmd, Lot, 0, Slippage, StopLoss, TakeProfit );
   }   
}
//+------------------------------------------------------------------+
今までの説明で、内容も大体理解できるのではないかと思いますが、前の説明と重複する部分を除いて説明していきます。

input int MaPeriod = 25;                              //移動平均の期間
input ENUM_MA_METHOD MaMethod = MODE_SMA;             //移動平均線の種類
input ENUM_APPLIED_PRICE AppliedPrice = PRICE_CLOSE;  //移動平均を計算する価格の種類
使用する移動平均線の条件です。入力パラメータとして宣言しています。

   // 1つ前と2つ前の足の終値を取得する
   double close1 = iClose( symbol, target, 1 );
   double close2 = iClose( symbol, target, 2 );
ここでまず終値を取得しています。
標準で用意されている、終値を取得するiClose()関数を使用しています。
iClose()関数の引数は以下のとおりです。
・string symbol
  終値を取得する通過ペア名。
・int timeframe
  終値を取得する時間軸。時間軸は、以下のように定義されています。
  ENUM_TIMEFRAMES列挙型
・int shift
  終値を取得する足の位置。(0~)

最後の引数は0から始まる足の位置を指定しますが、数字が小さいほど新しい足を表すため0が最新の足になります。
ここで最新の足とは、現在形成中の足のことですので、取得される値が変わる事があります。
既に確定済みの値を使用したい場合、1が確定済みの最新の足ということになります。
このコードでは1つ前と2つ前なので、確定済みの最新2本の終値を取得しています。

なお、チャートの価格を取得する関数は、iClose()の他にもiOpen()、iHigh()、iLow()があります。
名前から分かると思いますが、それぞれ始値、高値、安値を取得する関数です。使い方はiClose()と変わりません。

   // 1つ前と2つ前の移動平均の値を取得する
   double ma1 = iMA( symbol, target, MaPeriod, 0, MaMethod, AppliedPrice, 1 );
   double ma2 = iMA( symbol, target, MaPeriod, 0, MaMethod, AppliedPrice, 2 );
ここでは移動平均の値を取得しています。
標準で用意されている、移動平均を計算するiMA()関数を使用しています。
iMA()関数の引数は以下のとおりです。
・string symbol
  移動平均を計算する通過ペア名。
・int timeframe
  移動平均を計算する時間軸。時間軸は、以下のように定義されています。
  ENUM_TIMEFRAMES列挙型
・int period
  移動平均の期間。
・int ma_shift
  移動平均線をシフトする本数。
・ENUM_MA_METHOD ma_method
  移動平均線の種類。種類は、以下のように定義されています。
  ENUM_MA_METHOD列挙型
・ENUM_APPLIED_PRICE applied_price
  移動平均を計算する価格の種類。種類は、以下のように定義されています。
  ENUM_APPLIED_PRICE列挙型
・int shift
  移動平均の値を取得する足の位置。(0~)

終値と同じように1つ前と2つ前の位置の移動平均を取得しています。
また引数として渡す条件が、入力パラメータとなっています。

なお、標準で用意されているテクニカル指標を取得する関数も、iMA()の他にもたくさん存在します。以下を参考にしてください。
テクニカル指標関数

   int orderCmd = OP_NONE;
   if ( close1 > ma1 && close2 < ma2 )
   {
      // 終値が移動平均線を上抜けたら買う
      orderCmd = OP_BUY;
   }
   else if ( close1 < ma1 && close2 > ma2 )
   {
      // 終値が移動平均線を下抜けたら売る
      orderCmd = OP_SELL;
   }
ここはコメントにも書いてあるので分かると思いますが、上抜けた場合と下抜けた場合に、それぞれの注文の方向を決めています。
close1とclose2が1つ前と2つ前の終値、ma1とma2が1つ前と2つ前の移動平均線の値なので、直近のclose1がma1を超えていて、その前のclose2がma2未満の場合、上抜けたと判断しています。
下抜けはその反対ですね。

後はorderCmdに代入された注文種別によって取引を行っています。この辺は簡単EAとほとんど変わりませんので分かるかと思います。

コンパイルが通ったら、バックテストをしてみましょう。

赤い線が移動平均線で、上抜けと下抜けで売買していることが確認できます。
成績は見てもらえば分かるので載せませんが、まぁずいぶん負けてますw

こんな単純な戦略だけで勝てたら誰も苦労しないという話ですよね。実際は独自の複雑なインジケータなども組み合わせながら戦略を立てるかと思います。
なので次は、独自のインジケータを使ったEAを作ってみたいと思います。

次の回へ

「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ MetaTraderへ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。

2016年5月6日金曜日

[MQL超入門] その019 クラスとインスタンス

簡単EAの説明で、さんざん注文クラスというものが出てきました。さらにはインスタンスという言葉も出てきました。
「クラス」と「インスタンス」とは何か、そしてなぜ注文クラスというものを使っているのでしょうか。その辺を説明していきたいと思います。

なお、クラスを作らなくてもMQLでプログラムはいくらでも作れます。
より本格的なプログラムらしく作るための考え方ですので、興味のない方は読み飛ばしてください。


まず一般的によく言われるのは、
クラスが「型」で、その「実体」がインスタンス。
というものです。
さらにはクラスは「設計図」「雛形」などとも言われます。

という説明だけで分かれば苦労はしないので、もう少し分かりやすくいきましょう。

これもよく取り上げられる例ですが、「車」に例えましょう。
まず先に図で見てみます。
車の設計図が「クラス」。それを元に実際に作られた車が「インスタンス」です。

当たり前ですが、車の設計図だけでは車として乗ることはできません。
実際に作られて車という「モノ」になって初めて乗ることができます。設計図の時点では車としての役には立ちません。

では設計図は何のためにあるのでしょう。設計図は必要ないのでしょうか?
もし設計図がなければ、全く同じものは作れないでしょう。
既に作られた1台を元に、何とかそれらしいものを作ることは出来るかもしれませんが、その全ての車が全く同じに作られていて、全く同じ動きをする保障はできないでしょう。何か問題があった時に、解決するのにも大変な労力がかかることは容易に想像できると思います。


同じ車の設計図からは、当然同じ車が作られます。
ただし同じ設計図から作られていても、作られた2台の車は当然別の「モノ」です。
車Aのアクセルを踏んだら、車Bも一緒に動くなんてこともありません。

つまり車としての作りや機能は保障しながら、「モノ」としては完全に独立させることができます。


さらに、運転者はアクセルを踏んだらなぜ車が進むのかを知っている必要はありません。
内部的な仕組みを知らなくても、使い方を知っていれば使える、というは大きなメリットです。
仕組みがどうなっていて、どのような根拠があって、どのような試験をしてきたか、というのはメーカーが情報を持っていて、必要があれば提供できればそれでよいでしょう。

注文クラスに話を戻すと、例えばSendOrder()は「注文をする」という関数ですが、その中では証拠金不足のチェックやトレードが許可されているか等、様々な考慮をしています。しかし使う側にはそのことを一切知らなくても、全て考慮された上で注文することができます。

複雑な処理でも使う側はそのことを知らずにシンプルに使える、というようにプログラムを作れるということです。


このように、ただ想定どおり動くだけでなく、問題が発見しやすい、後から修正がしやすいなども考えることができると、質の高いプログラムが作れると思います。
ものすごーーーく簡単に、色々すっ飛ばして説明したので、興味のある方は「オブジェクト指向」というものを調べてみてください^^;

次の回へ


「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ MetaTraderへ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。

2016年5月2日月曜日

[MQL超入門] その018 とりあえずやってみよう!簡単EA作成5

作成した簡単EAのコード説明の後半です。
長くなりましたが、これで簡単EA作成は終わります。

さて、いよいよ実際に取引を行うコードを見てみます。
   bool hasPosition = ( m_wrapper.GetPositionCount() > 0 );
   if ( !hasPosition )
   {
      // ポジションを保持していなければ注文する
      int orderCmd = ( isBuy ? OP_BUY : OP_SELL );
      m_wrapper.SendOrder( orderCmd, Lot, 0, Slippage, StopLoss, TakeProfit );

      // 売/買を反転      
      isBuy = !isBuy;
   }
   else
   {
      // ポジションを保持していれば決済する
      m_wrapper.CloseOrder( 0, Slippage );
   }
順を追って見ていきましょう。

   bool hasPosition = ( m_wrapper.GetPositionCount() > 0 );
注文クラスのGetPositionCount()関数を呼んで、保有ポジションがあるかどうかを判断しています。
GetPositionCount()関数は名前のとおり保有ポジション数を取得する関数です。
ここで、取得した数が0より大きいかというif文のような書き方をしていますが、これはこの条件を満たすかどうかのbool型の値を返すという意味になります。

bool型が分からない方はこちらを見てください。
[MQL超入門] その007 変数の型

   if ( !hasPosition )
ここで、!(びっくりマーク)が出てきました。
これは論理否定演算子、NOT演算子と言い、論理(bool)値を反転させるという意味です。
この場合、hasPositionがfalseの場合に条件を満たすことになりますので
   if ( hasPosition == false )
と同じ意味になります。
つまり保有しているポジションがない場合、という条件です。

      int orderCmd = ( isBuy ? OP_BUY : OP_SELL );
今度は?(はてなマーク)が出てきました。
これは三項演算子と言い、?の前の条件が真の場合は1つ目、偽の場合は2つ目の値を返すという意味なので、
      int orderCmd;
      if ( isBuy )
      {
         orderCmd = OP_BUY;
      }
      else
      {
         orderCmd = OP_SELL;
      }
と同じ意味になります。
実はこの書き方には、「1行で書けるから良い」「いや読みにくいから嫌だ」など賛否ありますが、個人的にはすっきり書けるので好きです。

さて、ここでorderCmdに代入している値が、今から行う取引の種類です。
OP_BUYが成行買い、OP_SELLが成行売りです。
急に取引のプログラムらしくなってきましたね。

      m_wrapper.SendOrder( orderCmd, Lot, 0, Slippage, StopLoss, TakeProfit );
注文クラスのSendOrder()関数が、実際に注文を行う命令です。
引数は以下のとおりです。
・int cmd
  売買種別。種別には以下のものがあります。
   ・OP_BUY : 成行買い
   ・OP_SELL : 成行売り
   ・OP_BUYLIMIT : 指値買い
   ・OP_SELLLIMIT : 指値売り
   ・OP_BUYSTOP : 逆指値買い
   ・OP_SELLSTOP : 逆指値売り
・double volume
  売買ロット。
・double price
  注文価格(成行き時は現在値を自動設定)。
・uint slippage
  許容スリッページ(Pips単位)。
・uint stoploss
  ストップロス(Pips単位)。
・uint takeprofit
  利益確定値(Pips単位)。

売買種別と注文価格以外は、入力パラメータとしています。(このEAでは成行注文しかしないので注文価格は固定で0としています)
このへんもパラメータになっていたほうが後で調整しやすいので、こうしておくのがよいと思います。

      // 売/買を反転      
      isBuy = !isBuy;
買いと売りを交互に行わせるために売買のフラグを反転させています。これも特に意味はありません。

      // ポジションを保持していれば決済する
      m_wrapper.CloseOrder( 0, Slippage );
最後が、決済を行う命令です。注文クラスのCloseOrder()関数で決済をします。
引数は以下のとおりです。
・int index
  決済するポジションのインデックス。(0~)
・uint slippage
  許容スリッページ(Pips単位)。

注文クラスでは、保有ポジションのリストを注文した順に管理しています。
1つめの引数には、0から始まるこの順番で、どのポジションを決済するかを指定します。このEAでは必ず1ポジションしか持たないので、固定で0を指定しています。
また、注文クラスにはCloseOrderAll()という全てのポジションを決済する関数もありますので、全決済の場合にはこちらを使っても構いません。

という事で、やっと一通りの説明が終わりました。
取引自体は結局はSendOrder()とCloseOrder()を呼ぶだけなので、あとはどういうルールで取引するか、つまりどんな条件でこれらの関数を呼ぶか、という話になります。

このEAではその取引ルールが適当で役に立ちませんので、これからはテクニカル指標などを使いながら取引するプログラムを書いていきましょう。

次回はひとまず、説明を飛ばしていたクラスとインスタンスについてのお話をしようかなと思います。

次の回へ

「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ MetaTraderへ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。

2016年4月27日水曜日

[MQL超入門] その017 とりあえずやってみよう!簡単EA作成4

本格的なEA作成に入る前に、今まで省いていた前回までのコードの説明をしたいと思います。

まずは準備として最初に追加した部分の説明から。

#include <Custom/TradingWrapper.mqh>
まず、この一文です。
前に書いたとおり、本ブログではEAに注文クラスというものを使用しています。その注文クラスが定義されているソースファイルを使いますよ、という指定になります。

このように別のソースファイルなどを読み込んで使用することを「インクルード」と言います。
ソースコードの中でよく必要になる汎用的な関数や定数宣言などを機能ごとにファイルにまとめておき、必要に応じてインクルードすることで、ソースファイルの役割分担がはっきりしたり、1つ1つのソースファイルの中身をすっきりさせることができるんですね。

input int MagicNumber = 37858260; //マジックナンバー 他のEAと当らない値を使用する。
input int MaxPosition = 1;        //最大ポジション数
input double SpreadFilter = 2;    //最大スプレット値(PIPS)
// トレード補助クラス
CTradingWrapper m_wrapper(Symbol(), MagicNumber, MaxPosition, SpreadFilter);
上はインジケータでも出てきた入力パラメータですね。
下が、たびたび話に出てきた注文クラスの宣言と、同時にインスタンスを生成しています。
インスタンスとは実体のことですが、クラスやインスタンスについては別に説明したいと思います。

余談ですが、今までずっと注文クラスと書いてきましたが、コメントはトレード補助クラスとなっていて統一性がないですね。すいません^^;

注文クラス生成の引数は以下の通りです。
・string symbol
  通貨ペア。Symbol()関数は、現在の通貨ペアを返します。
  Symbol()関数
・int magicNumber
  識別(マジック)番号。EAごとに異なる番号を指定します。
・int maxPosition
  最大ポジション数。保有ポジション数がこの数を超えるようになる取引はしない。
・double spreadFilter
  最大スプレッド値。この値よりもスプレッドが大きい場合は取引しない。

この引数に、入力パラメータを使用しています。
例えば最大ポジション数ですが、このEAの場合は必ず1ポジションしか持たないようにしているのでいくつを指定しても同じなんですが、この辺はパラメータとしておいたほうが何かと使いやすいので、こういうものとしておいていいと思います。

では、取引に関する部分を説明していきます。
取引の内容はOnTick()内に記述します。
OnTick()は、価格の更新がある度に呼ばれる関数です。

   string symbol = Symbol();
   static ENUM_TIMEFRAMES target = PERIOD_H1;
   static datetime before = 0;

   // 1時間の確定足でのみ動作
   datetime current = iTime(symbol, target, 0);   
   if( before == current )
   {
      return ;
   }
   before = current;
最初のこの部分は、1時間足が確定し、次の1時間足の形成が始まった時にのみ処理を行うための記述です。

ここで使用しているiTime()関数は、足の形成開始時刻を取得する関数です。
iTime()関数の引数は以下の通りです。
・string symbol
  足の形成開始時刻を取得したい通貨ペア名。
・int timeframe
  足の形成開始時刻を取得する時間軸。時間軸は、以下のように定義されています。
  ENUM_TIMEFRAMES列挙型
・int shift
  形成開始時刻を取得したい足の位置。(0~)
  現在形成中の足の場合は0、1本前の場合は1、2本前の場合は2、となります。

このコードでは、時間軸を1時間とし、足の形成開始時刻が前回から変わるまでは処理をせず終了してしまいます。そのため、1時間おきの足の形成が始まった時のみにその後の処理を行います。
このEAでは足の確定時で十分なのでこうしていますが、EAによっては足の形成中でも処理を行う必要があると思いますので、必ず必要な記述ではありません。

   counter++;
   if ( counter < 5 )
   {
      // 5本に1回しか処理しない
      return;
   }   
   // カウンターをリセット
   counter = 0;
ここでは、1時間に1回の処理をさらに5回に1回のみ処理するようにして、5時間ごとに取引をするようにしています。
結果を分かりやすくするために5時間ごとにしただけなので、特に意味はありません。

   m_wrapper.RefreshPositions();
これは注文クラス独自の処理です。
注文クラスが管理している保有中ポジションのリストを最新の状態に更新しています。
ストップロスや利益確定などで自動的にポジション決済などが行われることがありますので、OnTick()で処理を行う前には必ずこれを呼ぶというお約束事と思ってください。

さてここからがやっと実際に取引するコードになるんですが、長くなってしまったので次回にしたいと思います。
実際の取引以外のところはサッと書いて終わろうと思ったのに、思ったよりも長々と書いてしまいました^^;

次の回へ


「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ MetaTraderへ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。