システムの質を高める

システムの質を高める


 前回までに作ったシステムに手を加えて、実際の取引の時に使いやすいものにしていきましょう。

  1. パラメータを作る
  2. 売買制限を設ける
  3. 他の取引(手動での取引や他のシステムでの取引)の邪魔をしないようにする

の順にやってみます。

1.パラメータを作る方法

パラメータとは、外部から設定できる値のことで、これを設けることでシステムに融通性が出てきます。

今回は、

  • 損切り幅
  • 利食い幅
  • ロット数
  • 移動平均の計算期間

を、設定可能にしていきましょうか。

書く場所は、#property ・・・ の下あたりに書きましょう。

書き方は、

extern int     StopLoss    = 40;
extern int     TakeProfit  = 20;
extern double  lots        = 0.1;
extern int     MaPeriod    = 12;

という感じです。

変数の宣言の前に extern を付け加えるだけです。
宣言の後に = ** と付け加えると、最初は**を入れておいてくださいといった意味になります。

さて、この変数たちをシステム内に忍ばせるわけですが、
まずはOrderSend の()内の損切り値に入れたいと思います。

その前に、パラメータで扱う変数は、『損切り幅』ですから、これを『損切り値』に変えてあげなければなりません。

Ask - (StopLoss * Point)

と書けば損切り値に変わります。
Point とは、このチャートの1ピプスの桁のことで、例えば、USDJPYの通貨ペアだったら、0.01になります。
(小数点以下3桁まで表示されている場合は、0.001になります)

これは、
買値 - ( 損切り幅 * ピプスの桁 )

といった感じです。

利食い値も同様に

Ask + (TakeProfit * Point)

となります。

ロット数はlotsのままで大丈夫そうですね。

これを、OrderSend の()内にそれぞれ入れてあげましょう。

   OrderSend(Symbol(), OP_BUY, lots, Ask, 3, Ask-(StopLoss*Point),
                      Ask+(TakeProfit*Point), "Buy", 10, 0, Blue);

といった具合です。

同じように移動平均の関数 iMA の()内にも MaPeriod を入れてあげましょうか。

   ma = iMA(NULL, 0, MaPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);

というように、10のかわりに MaPeriod を入れましょう。

さて、これでパラメータを設定できる環境になりました。

コンパイルして、一度、MT4で作動させてみましょう。
やり方は、使い方を覚えるの記事の中で紹介しましたね。

最初にプロパティウィンドウが開きました。
その中のパラメータータブをクリックすると、設定した4つのパラメータが並んでいます。
Value の欄の数字をダブルクリックするとパラメータを入力し直すことができます。

これで、システムの幅が広がったわけです。

2.売買制限を設ける方法

今回は、一つのバーの中では1回しか注文を出さないという制限を付け加えましょう。

ここで、バーとは、ロウソク足の一本一本のことです。
そして、Bars という変数は、現在あるバーの数です。

このバーの数が変化していないうちは、まだ新たにバーが現れていないということがわかります。

この条件を付け加えると、注文したバーの中で、損切りや利食いが発生したとき、次のバーが現れるまで注文をしないといった意味になります。

まずは、バーの数を記憶させる変数を nowbar としておきましょうか。

これを、パラメータを設置した下あたりに宣言します。

int nowbar;

としましょう。
これを、int start() の{}内に書いてしまうと start() の動作が終わるたびにリセットされてしまうので、記憶しておいてもらえません。
気をつけましょう。

この変数 nowbar に、取引したときのバーの数を記憶させましょう。

やり方は、OrderSend 文の下の段に

      nowbar = Bars;

と、書きます。
nowbar に現在のバーの数を代入するといった意味です。

次に、nowbar と Bars が違うときだけ取引するという条件を加えましょう。

やり方は、OrderSend 文の上のif文の()内を

   nowbar != Bars && Close[1] > ma

とするのです。

これは、
nowbar が Bars と同じでない時、かつ、1本前の終値が ma より大きい時、
という意味です。

!= はノットイコール(~ではない)の意味です。

これで一つのバーの中では、1回しか注文を出さないようになりました。

3.他の取引の邪魔をしないようにする

他の邪魔をしないようにするには、このシステム特有の暗号を作ってあげましょう。

これは、OrderSend のマジックナンバーを使います。

まずは、ポジション無しの場合に使った

   if(OrdersTotal() < 1)

の代わりに

OrdersTotal()のなかで、マジックナンバーが 10 のものがあればそのインデックスを、変数 CurrentPosition に代入する

というプログラムを書き、代入されなかった場合は、ポジション無しとみなす、といった文にしていきましょう。

ここでは、新たに cnt と CurrentPosition という変数をつくりますので、int start() の{}内の ma を宣言した下あたりに

   int cnt, CurrentPosition = -1;

と変数の宣言をしてあげましょう。

ここで、CurrentPosition = -1 としたのは、
ポジションのインデックスが 0 から始まるので、変数をそのまま宣言したときのデフォルトの 0 とカブらないようにするためです。

そして、オーダーの条件文の前に

   for(cnt = 0; cnt < OrdersTotal(); cnt++)
   {
      OrderSelect(cnt, SELECT_BY_POS);
      if(OrderMagicNumber() == 10) CurrentPosition = cnt;
   }

という文を書いてあげます。

ここで、
for(*1; *2; *3){***}
は、繰り返すときに使う構文で

*1から開始して***する。*2のうちは*3を行い、***する。また、*2のうちは*3を行い、***する。また、・・・。

という感じに繰り返していきます。

また、cnt++ とは、cnt=cnt+1 という意味で、cnt に 1 をたして、cnt に代入するといった意味です。

全体では、

cnt が 0 のとき、
0 よりポジションの合計が多いなら続けてください。
持っているポジションのうちインデックス 0 のものを選択してください。
もし、そのマジックナンバーが 10 であるならば、CurrentPosition に 0 を代入してください。
cnt に 1 を足してください。
cnt が 1 のとき、・・・

と続きます。
もし、ポジションの合計が 0 より多くない(0の)とき、{}内の処理はされないので、CurrentPosition には、何も代入されることが無く、デフォルトの -1 になるということになります。

ですので、
if(OrdersTotal() < 1) の代わりに

   if(CurrentPosition == -1)

と書いてあげましょう。
if(CurrentPosition < 0) としてもいいですね。

さらに、ポジション有りの場合のOrderSelect のインデックスの部分には、
0 の代わりに
CurrentPosition
と入力しましょう。

これで、このシステム特有のマジックナンバー 10 だけを選んで取引できるようになりました。
コンパイルを忘れずにしましょう。

int init() や int deinit() は今のところ使わないので消しても大丈夫です。

コード全体をコピペしてみます。

//+------------------------------------------------------------------+
//|                                                 HeikinSample.mq4 |
//|                                                            keiji |
//|                                  http://www.dr-ea.com/meta-blog/ |
//+------------------------------------------------------------------+
#property copyright "keiji"
#property link      "http://www.dr-ea.com/meta-blog/"

//パラメータ用の変数
extern int     StopLoss    = 40;
extern int     TakeProfit  = 20;
extern double  lots        = 0.1;
extern int     MaPeriod    = 12;

//記憶用の変数
int nowbar;

//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
{
//----
   //変数の宣言
   double ma;
   int cnt, CurrentPosition = -1 ;
   
   //移動平均値の算出  
   ma = iMA(NULL, 0, MaPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
   
   //オーダー
   for(cnt = 0; cnt < OrdersTotal(); cnt++)
   {
      OrderSelect(cnt, SELECT_BY_POS);
      if(OrderMagicNumber() == 10) CurrentPosition = cnt;
   }

   // ポジション無しの場合
   if(CurrentPosition == -1)
   {
      //もし1本前の終値が移動平均より上ならば
      if(nowbar != Bars && Close[1] > ma)
      {                          
         //ポジションを取る。
         OrderSend(Symbol(), OP_BUY, lots, Ask, 3, Ask-(StopLoss*Point), 
                                                      Ask+(TakeProfit*Point), "Buy", 10, 0, Blue);
         nowbar = Bars;
      }
   } 
   // ポジション有りの場合
   else 
   {
      //ポジションのデータを取得。
      OrderSelect(CurrentPosition, SELECT_BY_POS);
      //もし1本前の終値が移動平均より下ならば
      if(Close[1] < ma)
      {
         //ポジション決済
         OrderClose(OrderTicket(), OrderLots(), Bid, 3, Green);
      }
   }
//----
   return(0);
}
//+------------------------------------------------------------------+

こんな感じになりましたか?
今回はこのへんで。

完全自動売買への道のり

タグ

2007年10月14日|コメント (10)

カテゴリー:完全自動売買

コメント (10)

初めまして

シストレを始めようと思い、ネット上で情報を検索してたら、このHPに辿り着きました。
簡単に自己紹介しますと、プログラム経験はありますが(一応、SEです)、MQLはまったくの素人です。
サンプルで、ポジション無しの場合、nowbar != Bars の比較を行ってますが、nowbarの値が確定するのはOrderSend()の後です。nowbarは初期化されてませんので、この比較の結果はどうなるのでしょうか?

宜しくお願いします。

投稿者:MT4初心者 |2013年3月26日 17:09

こんにちわ^^

>MT4初心者さん

初期値がない整数変数は、0が割り当てられますので、
OrderSend()実行前のnowbar != Barsはtrueとなります。

グローバル変数を初期化する場合は、init()関数内で初期値として値を代入するようにしたほうがよいです。
(パラメータ変更や稼動時間足変更した際、init()関数は実行されますがグローバル変数は初期化されないため。)

投稿者:星野慶次 |2013年3月26日 17:44

慶事さん

ご回答ありがとうございました。初期値なし変数が自動で0に初期化されるなんて、MQLは便利ですね。

Start()について質問があります。前の章で、Start()は「そのチャートに動きがあったときに、読み取られるプログラムです」とありました。これは、Tick単位で呼び出されると理解してます。値動きが激しいときは、Start()は頻繁に呼び出されると思います。Tickの変化が発生してStart()が呼び出され、最初のStart()の処理が完了しないうちにTickが変化した場合は、2回目のStart()は呼び出されてしまうのでしょうか?
例:Tick変化 → 1回目のStart開始 → Tick変化 → 2回目のStart開始 → 2回目のStart終了 → 1回目のStartの残り部分が実行 となり、予期せぬ結果を招く恐れがあります。

それとも、1回目のStart()処理中に、Tickの変化があっも、1回目のStart()が完了するまでは、2回目のStart()は呼び出されない仕組み(排他制御)になってるのでしょうか?

宜しく御願いします。

投稿者:MT4初心者 |2013年3月28日 00:59

こんにちわ^^

start()関数についてですが、後者のほうですね。
1回目のstart()関数処理中は次のティックがあっても無視されます^^;

投稿者:keiji Author Profile Page|2013年3月28日 08:03

いつもMqlいじる時は、参考にさせて頂いております。
ありがとうございます。

投稿者:Aya |2013年8月11日 23:52

こんにちわ!

Ayaさん>

コメントありがとうございます^^
お役にたててよかったです^^

投稿者:keiji Author Profile Page|2013年8月12日 13:54

はじめまして
自分でEAを作成したくて調べていたらここをみつけました。
非常にわかりやすくてたすかりました。
質問なのですが、最初に買いで損切り、利益確定を10ピップでエントリーし、
その後、損切りされればすぐに反対の売りでエントリー
利益確定されれば、また買いでエントリーする
というのを延々と繰り返していきたいのですが、
どういうEAにしたらよいか教えていただけませんか

よろしくお願いします。

投稿者:egami |2013年8月12日 20:53

こんにちわ!

egamiさん>

TPやSLにせず、EA内部で決済するようにすると編集しやすいかと思います。

TP・SLで決済する場合は、ポジションの有無を調べ、保有ポジションがなければ、取引履歴から前回の決済がTPかSLを調べるといった手順がよいかと思います。TPかSLかはOrderComment()で判別可能です。

あと、保有中の損益を記録しておいて、決済前の損益がプラスならTP決済・マイナスならSL決済と判断してもよいかもしれません。

投稿者:keiji Author Profile Page|2013年8月14日 20:18

いろいろなサイトがありますが、貴サイトは分かりやすいです。

初めてなのにいきなり(駄)質問で申し訳ないのですが

とあるインジケータを改造してEAを作ったのですが、処理がint start()
に行く前に終了してしまいます。

MT4のバックテスターでテストしていますが、プログラムにミスがあるのか
テスターの使い方に問題があるのか分かりません。

お時間がありましたら、なにかしらご教示いただけたら嬉しいです。
よろしくお願いいたします。

投稿者:hiro |2016年12月 4日 13:36

先ほど質問したものですが、解決しましたのでご報告いたします。

投稿者:hiro |2016年12月 4日 21:50

コメントを投稿する

(初めてのコメントの時は、コメントが表示されるためにこのブログのオーナーの承認が必要になることがあります。承認されるまでコメントは表示されませんのでしばらくお待ちください)






画像の中に見える文字を入力してください。

Captchaの認証で入力ミスがありますと、コメントが消えてしまいますのでご注意ください。
コメント欄に(X)HTMLタグやMTタグを記述される場合、「<」は「&lt;」、「>」は「&gt;」と入力してください。