文系人間のためのシステムトレーダー養成所 メタトレーダー講義第15回初めてのエキスパートアドバイザー第3部
--------

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
2009-04-08

メタトレーダー講義第15回初めてのエキスパートアドバイザー第3部

この文章はCodersGuruさんのMQL4Lesson4の訳文です。(翻訳しての転載に本人の許可はとってあります)         
                      MQL4講座       
                     Coders'guru                               
                      www.forex-tsd.com                          
                           -15-
                 初めてのエキスパートアドバイザー
                   第3部


最近の二つの講義では、初めてのエキスパートアドバイザーを紹介し、その背後にあるアイディアを知りました。
また、第2回目の補講2では、今回の授業で使うことにもなるトレーディング関数について学びました。

今回は、エキスパートアドバイザーの残りのコードの解読の続きになります。
今回の我々の解読任務によって、貴方の疑問がきれいになくなることを期待しています。

私たちのコード

//+------------------------------------------------------------------+
//| My_First_EA.mq4 |
//| Coders Guru |
//| http://www.forex-tsd.com |
//+------------------------------------------------------------------+

#property copyright "Coders Guru"
#property link "http://www.forex-tsd.com"

//---- 埋め込まれた設定値
extern double TakeProfit=250.0;
extern double Lots=0.1;
extern double TrailingStop=35.0;
//+------------------------------------------------------------------+
//| expert initialization function                    |
//+------------------------------------------------------------------+

int init()
{
//----

//----

return(0);
}
//+------------------------------------------------------------------+
//| expert deinitialization function                   |
//+------------------------------------------------------------------+

int deinit()
{
//----

//----
return(0);
}
int Crossed (double line1 , double line2)
  {
   static int last_direction = 0;
   static int current_direction = 0;

   if(line1>line2)current_direction=1; //アップ
   if(line1<line2)current_direction=2; //ダウン

   if(current_direction !=last_direction) //変化
   {
      last_direction = current_direction;
      return(last_direction);
   }
   else
   {
     return (0);
   }
 }
//+------------------------------------------------------------------+
//| expert start function                     |
//+------------------------------------------------------------------+

int start()
 {
 int cnt, ticket, total;
 double shortEma,longEma;

 if(Bars<100)
 {
  Print("bars less than 100");
  return(0);
 }
 if(TakeProfit<10)
 {
  Print("TakeProfit less than 10");
  return(0); //check TakeProfit
  }

 shortEma=iMA(NULL,0,8,0,MODE_EMA,PRICE_CLOSE,0);
 longEma=iMA(NULL,0,13,0,MODE_EMA,PRICE_CLOSE,0);

 int isCrossed = Crossed(shortEma,longEma);

 total = OrdersTotal();
 if(total < 1)
  {
  if (isCrossed == 1)
   {
ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,0,Ask+TakeProfit*Point,
"My EA",12345,0,Green);
     if(ticket>0)
     {
     if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES))
Print("BUY order operand : ",OrderOpenPrice());
     }
    else Print("Error opening BUY order : ",GetLastError());
     return(0);
     }
    if(isCrossed == 2)
    {

     ticket=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,0,
     Bid-TakeProfit*Point,"My EA",12345,0,Red);
      if(ticket>0)
        {
        if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES))
      Print("SELL order opened ; ",OrderOpenPrice());
        }
      else Print("Error opening Sell order : ",GetLastError());
       return(0);
       }
       return(0);
    }
     for(cnt=0;cnt<total;cnt++)
     {
     OrderSelect(cnt,SELECT_BY_POS,MODE_TRADES);
     if(OrderType()<=OP_SELL && OrderSymbol()==Symbol())
    {
    if(OrderType()==OP_BUY) //ロングポジションでエントリー
    {
    //手仕舞うか?
    if(isCrossed ==2)
      {
         OrderClose(OrderTicket(),OrderLots(),Ask,3,Violet);
  //手仕舞い
     return(0); //次へ
      }
    if(TrailingStop>0)
     {
     if(Bid-OrderOpenPrice()>Point*TrailingStop)
       {
       if(OrderStopLoss()<Bid-Point*TrailingStop)
        {
         OrderModify(OrderTicket(),OrderOpenPrice(),Bid-
          Point*TrailingStop,OrderTakeProfit(),0,Green);
         return(0);
         }
        }
       }
      }
     else//ショートポジションへ
      {
     //手仕舞うべきか
      if(isCrossed ==1)
      {
      OrderClose(OrderTicket(),OrderLots(),Ask,3,Violet);
  //手仕舞い
     return(0); //決済
      }
     //トレイリングストップのチェック
      if(TrailingStop>0)
       {
        if((OrderOpenPrice()-Ask)>(Point*TrailingStop))
        {
         if((OrderStopLoss()>(Ask+Point*TrailingStop)) ||
      (OrderStopLoss()==0))
             {

    OrderModify(OrderTicket(),OrderOpenPrice(),Ask+Point*TrailingStop,
     OrderTakeProfit(),0,Red);
            return(0);
             }
            }
           }
          }
         }
       }
 return(0);
 }
//+------------------------------------------------------------------+

____________________________________________________________________
int cnt, ticket, total;

この文では、3つのintegerタイプの変数を宣言しています。
一つの文を使うことで、3つの変数を宣言していますが、これは三つの変数(cnt,ticket,total)がどれも同じデータタイプなので可能になっています。(もしデータタイプが違っていたら、一つの文でまとめて宣言することはできません。)

メモ:複数の変数を一つの文で宣言するために、変数のデータタイプを示す宣言キーワードから文を始めて、識別子(変数名)をコンマで区切っていきます。
上のコード文を、3行に分けて書くことも可能です。下のようになります。
int cnt;
int ticket;
int total;

cnt変数はオープンオーダー(指定した値段になると実行される注文)のループ回数をチェックする変数として使用することになります。
ticket変数はOrderSend関数によって返されたチケット番号(注文番号)の値を入れる変数として使用することになります。
total変数は、すでに注文された総数の値を入れる変数として使用することになります。
____________________________________________________________________
double shortEma,longEma;

ここでは再び1行で、二つの変数を宣言しています。
この変数は短期EMAと長期EMAの値を入れる変数として使用することになります。
前回の部で説明したように、買いの状況なのか売りの状況なのか、手仕舞いの状況なのかという判断には、短期EMAと長期EMAの交差を使用しています。
____________________________________________________________________
if(Bars<100)
 {
  Print("bars less than 100");
  return(0);
 }

私たちは正常なチャートで働かせたいと思っています。正常なチャートは、100以上のバー(ローソク足)があることを想定しています。なぜなら、バーの数が不十分であると、EMAインディケーターが機能しないあるいは正常に動かないからです。

私たちはBars関数を使うことによって、チャート上に何本のバーがあるかを取得し、バーの数が100以下であるかそうでないかをチェックします。もし、バーの数が100より少なければ、二つのことをします。
エキスパートログに"bars less than 100"とメッセージを書くことで、何が問題なのかユーザーに伝えます。
その後、return(0)のコード文によってstart関数を終了します。

このようにして、チャート場のバーが100以下であったらばEA(エキスパートアドバーザー)を働かせることを終了させます。
図1
図1-エキスパートログ

____________________________________________________________________
if(TakeProfit<10)
 {
  Print("TakeProfit less than 10");
  return(0); //check TakeProfit
  }

私たちは、不十分なTakeProfit(獲得利益)の設定の状態でEA(エキスパートアドバイザー)を起動させたくもありません。
TakeProfit変数は、external(外部)変数ですので、ユーザーが初期設定値を、エキスパートアドバイザーのプロパティーウィンドウで変更することが可能です。
私たちはEAのユーザーを、ユーザーの誤った判断から守りたいと思います。
私たちはTakeProfit変数が10以下であることは悪い判断であると考えていますので、ユーザーがTakeProfit変数が10以下に指定したかそうでないかをチェックします。

もしも10以下であったなら、私たちは"TakeProfit less than 10"というメッセージを表示させることによって、何が問題であるかをユーザーにしらせます。そして、return(0)を返すことによってstart関数を終了させます。
____________________________________________________________________
 shortEma=iMA(NULL,0,8,0,MODE_EMA,PRICE_CLOSE,0);
 longEma=iMA(NULL,0,13,0,MODE_EMA,PRICE_CLOSE,0);

もしすべてが大丈夫なら、チャート上のバーの総数は100以上であり、TakeProfit変数は、ユーザーが10以上に指定しているということになります。

そこで私たちは今、現在のバーの短期と長期のEMAの値を算出したいと思います。
MQL4に埋め込まれている、移動平均線の値を算出するテクニカル指標関数であるiMAを使います。
私はここで数分立ち止まって、貴方にiMA関数の詳細を伝えなければなりません。

iMA

シンタックス
double iMA(sring,Symbol,int timeflame, int period,int ma_shift,int ma_method,int applied_price,int shift)

解説
iMa関数は移動平均線を算出し、その値を(doubleタイプで)返します。

  メモ:移動平均線とは、ある通貨の、ある時間足(日、時間、分)での、割り当てられた期間の平均
     値です。

設定値
この関数は7つの設定値を取ります。

string symbol:
貴方の取引での通貨ペアの指定(EURUSD,USDJPY等)です。
もしもチャートで使っている通貨ペアを指定したいのなら、設定値としてNULLを割り当ててください。
(例えばEURUSDに指定して、GBPJPYのチャートに起動させたら、チャートはGBPJPYを開いていてもEURUSDの移動平均線が表示されつづけることになります。)

int timeframe
移動平均線に使用する時間足の設定です。
以下の時間足が使えます。

定数        値    解説
PERIOD_M1    1     1分足
PERIOD_M5    5     5分足
PERIOD_M15   15    15分足
PERIOD_M30   30    30分足
PERIOD_H1    60    1時間足
PERIOD_M4    240   4時間足
PERIOD_D1    1440   日足
PERIOD_W1    10080  週間足
PERIOD_MN1   43200  月足
0          0     チャートで使われている時間足

もし使用されている時間足を使いたければ、を設定値としてお使い下さい。

  メモ:時間足の設定は、整数でも定数名ででも使うことができます。たとえば
     iMA(NULL,PERIOD_H4,8,0,MODE_EMA,PRICE_CLOSE,0);
     は
     iMA(NULL,0,8,0,MODE_EMA,PRICE_CLOSE,0);
     と等しいです。しかし、コードをはっきりさせるために、定数名を使用することをお勧めします。

int period:
移動平均線の算出に使う足はいくつかを設定するものです。

int ma_shift:
移動平均線をチャートの開始時から何本ずらしたいかを設定するものです。
0は移動なしを意味します(図2参照)
正の整数は右にシフトします(図3参照)
負の整数は左にシフトします(図4参照)

int ma_method;
移動平均線の算出に使いたい、移動平均線の種類を指定するものです。
以下の設定値を使うことができます。

定数       値   解説
MODE_SMA   0   単純移動平均線 
MODE_EMA   1   指数移動平均線
MODE_SMMA  2   平滑移動平均線
MODE_LWMA  3   線形加重移動平均線

int applied_price:
移動平均線を算出する際に使う値段をきめます。
以下の値を使用することができます。

定数          値    解説
PRICE_CLOSE     0   終値
PRICE_OPEN      1   始値
PRICE_HIGH      2   高値
PRICE_LOW      3   安値
PRICE_MEDIAN    4   高値と安値の平均値
PRICE_TYPICAL   5   高値と安値と終値の平均値
PRICE_WEIGHTED  6   高値、安値、終値、始値の平均値
図2
図2:ma_shift=0
図3
図3:ma_shift=10
図4
図4:ma_shift=-10
int shift:
現在のバー(ローソク足)から何本前の値を移動平均線の算出にしようするか。
が現在のバーを使用することを指定します。
(0にすると現在のバーが使われるので、移動平均線の値が次のバーがでるまで動き続けます。そうするとクロスしたり戻ったりという場面がでてきますので、僕としてはバーが算出されクロスが確定した状態が分かる1を設定することをお勧めします。1を設定すると最新のバーから一つ前のバーで移動平均線のクロスが確定していたらエントリーすることになります。)

____________________________________________________________________
shortEma=iMA(NULL,0,8,0,MODE_EMA,PRICE_CLOSE,0);
 longEma=iMA(NULL,0,13,0,MODE_EMA,PRICE_CLOSE,0);

さてこれで、上のコード文の意味が解りましたね。

私たちはShortEma変数の値を、
8日間で終値を使った、現在のバーでの指数移動平均。
というように設定したことになります。簡単に言うと8日指数移動平均線です。

LongEma変数の値は、
13日間で終値を使った、現在のバーでの指数移動平均。
というように割り振りました。簡単に言うと13日指数移動平均線です。

____________________________________________________________________
int isCrossed = Crossed(shortEma,longEma);

メモ:Crossed関数は二つのdoubleの値を設定値として持ち、integerの値を返却値として返します。
最初の設定値は監視して欲しいと思う一つ目のラインの値(私たちの場合は短期EMAになります)になり、二つ目の設定値は二つ目の監視してほしいラインの値(長期EMA)になります。
Crossed関数は、二つのラインをCrossed関数が呼び出される度にその二つのラインの状態を記憶するstatic変数の方向を保存することによって監視します。

 ・最新の方向が保存された時に何も変わらなければの値を返します。
 
・方向が変化(ラインが交差)し、かつ最初のラインが二つ目のラインの上にある時はを返します。

 ・方向が変化(ラインが交差)し、かつ最初のラインが二つ目のラインの下にある時はを返します。

ここでは、isCrossed変数をCrossed関数の値を入れる変数として宣言しました。これを使用してエントリーや手仕舞いの注文をします。
____________________________________________________________________

 total = OrdersTotal();
 if(total < 1)
  {
      ・・・・・・
  }

OrderTotalの返却値をtotal変数に割り振りました。

  メモ:OrderTotal関数は、オープン注文や未決注文の総数を返します。もしも0が返されれば、何も
     注文を発注していないことを表します。
     詳しくは補講第2回を読みなおしください。

その後、この数をチェックして、注文があるかないかを確認します。ifブロック内のコードは、total変数の値が既に入っている注文はないことを示す1より小さい値でなければ働きません。
____________________________________________________________________ 
if (isCrossed == 1)
   {
ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,0,Ask+TakeProfit*Point,
"My EA",12345,0,Green);
     if(ticket>0)
     {
     if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES))
Print("BUY order operand : ",OrderOpenPrice());
     }
    else Print("Error opening BUY order : ",GetLastError());
     return(0);
     }

短期EMAと長期EMAがクロスして、短期EMAが長期EMAの上の状況になったら買いをいれます。
OrderSend関数を使うことによって買いエントリーの注文を入れます。

  メモ:OrderSend関数は売り/買いのオープンオーダー(指定した値段になると実行される注文)か、
     決済注文に指定のために使います。
     もしその注文が成功すればチケット(注文)番号が返却値として返され、失敗すれば-1が返さ
     れます。

  シンタックス:
int OrderSend(string symbol, int cmd, double volume, double price, int slippage, double stoploss,double, double takeprofit, string comment=NULL,int magic=0, detetime expriation=0,color allow_color=CLR_NONE)
補講2に詳しい記述がありますのでよろしければお読み直しください。

これらが、OrderSend関数で使った設定値です。

symbol:
私たちはSymbol関数を使って現在使用している通貨ペア名を取得し、OrderSend関数に受け渡しています。

cmd:
ここでは、買いポジションのエントリーをしたいのでOP_BUYをつかっています。

volume
ここでは外部変数であるLots変数(extern double Lots=0.1;)を指定することでユーザーが自由に変更できるようにしています。

price
ASK関数を使って、現在の買値を取得し、OrderSend関数に引き渡します。

slippage
最大許容スリッページの値は3としました。

stoploss
ここでは0を設定し、ストップロスの値は設定しないことを示しています。

takeprofit
ユーザーが与えた(初期設定はextern double TakeProfit=250.0;により250)TakeProfitの値とPoint関数の返却値を掛けたものにエントリーしたAskの値を足しています。


    メモ:Point関数は、現在使用されている通貨ペアのpip値です。
    例えば、もしあなたがEURUSDでトレードしていたならpip値は.0001になり、EURJPYならpip値は
    .01になります。
    ですので、OrderSendもしくはOrderModify関数と一緒に使用する前に、貴方のstoplossとtake
    profitの値のpip値を切り替えなくてはなりません。

comment
私たちは"My EA"の文字絵rつをコメントとして使用しました。

magic
マジックナンバーは12345と設定しました。

expiration
有効期限は設定していませんので0を使用しました。

arrow_color
エントリーのマークを緑にしました(なぜならお金は緑だからです。←Codersさんは日本人じゃありません)

OrderSend関数はもし注文に成功すればチケットナンバーを返します。ですので、私たちは以下のコード文を使うことで注文が成功したかどうかをチェックします。

 if(ticket>0)

OrderSelct関数をつかって、チケット番号によって注文を選択し、選択された注文のエントリーの値段を返すOrderOpenPrice関数の前にチケット番号を選択しておきます。

すべてがOKなら、OrderSendは特定のチケット番号(0以上の)を返します。そして、OrderSelect関数が成功すれば、それはユーザーに良いニュースを知らせるいい機会になります。彼に"BUY order opend"というメッセージとともにエントリー時の値段を表示して知らせてあげましょう。

  メモ:OrderSelectとOrderOpenPriceの詳細については補講第2回をご覧ください。

もしOrderSend関数が-1を返したら、それは、注文のエントリーが失敗したことを意味します。私たちはユーザーにこの悪い知らせを"Error opening BUY order:"に加えて、GetLastError関数から取得したエラー番号を表示させることによって知らせましょう。そしてこの状況の時は、return(0)を使ってこのstart関数を終了させなければなりません。


____________________________________________________________________
 if(isCrossed == 2)
    {

     ticket=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,0,
     Bid-TakeProfit*Point,"My EA",12345,0,Red);
      if(ticket>0)
        {
        if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES))
      Print("SELL order opened ; ",OrderOpenPrice());
        }
      else Print("Error opening Sell order : ",GetLastError());
       return(0);
       }

ここでは対照的な筋書きです。短期EMAと長期EMAがクロスして、長期EMAの下に短期EMAがある場合で、その時は売りを行います。

OrderSend関数を使って売り注文でエントリーします。OrderSend関数のBuy設定値とSell設定値との違いを予測してもらっても良いですか? そうです!貴方にプレゼントとして100pips差し上げたい!

今からあげる設定値は変わっていません。

symbol
volume
slippage
stoploss
comment
magic
expiration


これらの設定値は変わりました。

cmd
OP_SELLを使っています。エントリー時に売りから入りたいからです。

price
Bid関数をつかって現在の売りねをOrderSend関数に受け渡します。

takeprofit
ユーザーが定めたTakeProfit変数の値にPoint関数の返却値の値を掛けて、その計算結果を売値の結果から引きます。

arrow_color

ここでは赤色に売りでのエントリーのマークの色を指定しました。(なぜなら私たちはお金が好きで、お金は緑色ですが、売りポジションの時は別の色を使いたかったからです。)

私たちは簡単にOrderSend関数が呼び出されたあと上記の設定値によって何が起こったかを繰り返し説明することができます。

OrderSendがもし成功したならチケット番号を返します。
この数字が0より大きいかどうかをチェックすることでエラーがないことを確かめます。
OrderSelect関数をつかってOrderOpenPrice関数を使う前に注文を選択します。
全てが大丈夫だったら、ユーザーにこの良いニュースを("Sell Order Opend")に、エントリー時の値段を加えてメッセージを表示します。

そうでなければ、OrderSend関数は-1を返します。私たちはユーザーにこの悪い知らせを"Error opening SELL order:"と、エラー番号を加えて彼に伝え、return(0)を使ってstart関数を終了させます。
____________________________________________________________________ 
-ちょっとまって!(あなた)
-貴方が今説明した上記のifブロックのコードの最後の後に一行コードがあるよ!
-どちらにですか(私)
-ほら、これだよ。

 return(0);

そう!良く気付いたね。デモ今回はpipsをあげることはできないよ。
これらのブロックを注意深く見て。

if(total < 1)
  {
  if(isCrossed == 1)
    {
     ・・・・・
    }
    if(isCrossed == 2)
    {
      ・・・・・・
    }
       return(0); ←ここ!
    }

もし短期EMAが長期EMAが上向きにクロスしたら買い注文でエントリー。
もし短期EMAが長期EMAが下向きにクロスしたら売り注文でエントリー。

それではもしクロスをまだしてなかったらどうしますか?それがこのコード文の仕事です。
もしもクロスがなければ、私たちはstart関数を終了します。
(isCrossedの値が1でも2でもなかった時です)
____________________________________________________________________
さぁ私たちは今回で買い、売りポジションのエントリーをみてきました。

次の講義では、残りのコードをチェックして、ストラテジーテスターについて詳しく述べていきたいと思います。

今回の講義は楽しんでいただけたでしょうか。

Coder'sGuru



今回もお疲れ様でした。
長かった講義もあと3つで終了です。
全部読み終えたらなんか気持ちいいですね(笑)

何か質問がございましたらお願いいたします。


今回も最後までお読み頂きありがとうございました。


押していただけると凄い励みになりますのでよろしくお願いします。 

人気ブログランキングへ

theme : FXでシステムトレード
genre : 株式・投資・マネー

コメントの投稿

管理者にだけ表示を許可する

大変判りやすい説明なので、多くの疑問がここで解けました。ありがとうございます。
さて、1点質問させて下さい。

OrderSend で、Ask+TakeProfit*Point,
として指定している場所がありますね。
Pointは、その通貨の最小単位が入るようですので、EURUSDでは、0.0001, EURJPYで、0.01になると、TPの値が変わってきてしまうような気がするのですが。
各通貨ペア毎にこの場所は、書き変える必要がありますか?

また、最近は小数点以下4桁のブローカーのほかに、5桁のブローカーも出てきました。このような場合にも、TPの指定を変える必要がありますか?

以上ご返事いただければうれしいです。

Re: タイトルなし

温かいコメントにご質問ありがとうございます。

そうですね。今調べてみましたが、最小単位が自動的にはいるみたいです。
最小単位が自動的に入ってくれるので、逆に通貨ごとに書き換える必要はなさそうです。

たとえばEURUSDのAsk現在1.3603だとします。5Pips上の値を指定したければ
Ask*point*5 =1.3603*0.0001*5 = 1.3608 になりますよね。

EURJPYが132.03 だとすると
Ask*point*5 =132,03*0.01*5 = 132.08  になります。 

EURUSDの1Pipsは0.0001
EURJPYの1pipsは0.01
になるのでpointが便利な訳です。

それと、最近のブローカーが小数点が1つ多く増やされているところがあります。
僕が使っているメタトレーダーもそうなのですが、EURJPYは132.083 のように小数点以下第3位まで表示されます。 そうなるとpointは0.001になってしまうので、1pips指定したい時はpoint*10 にしないとなりません。

そうしたブローカーですと、
EURUSDもきっちり1.36083 のように、桁が一つ増えているので、1pips指定したい時はpoint*10 でOKです。

・・なんか解りづらい回答になってしまった気がするのですが疑問は解決されましたでしょうか?

何か疑問点が残られていましたらまたご質問ください。

今度ともよろしくお願い致します^^

お礼

管理人さま 
返信ありがとうございます。理解できました。説明がほんとうにわかりやすくて助かります。また回答をメモとして本編にも追加していただいたのですね。

また他のサイトのように各項目毎にページが分かれていないので、スクロールするだけで多くの項目が一覧できて、これもとても便利だと思いますよ。これからも活用させていただきます。
ありがとうございました。

Re: お礼

FFOさん。 いろいろと励ましのお言葉ありがとうございます。
大変恐縮ですが嬉しいです。
メタトレーダーがお互い使いこなせるようになると良いですね。
遊びに来ていただくと嬉しいです。
また質問等ございましたら質問してくださいね。

今後ともよろしくお願いします^^
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。