【超入門】MQL5 EA講座 第69回「簡単な仕組みのMT5用EAを作るーその2ー」【EAの作り方】

MQL5でEA作ろう講座

——

前回から実際のEA(自動売買プログラム)作りに入りました。

前回までに行った作業は以下の通りです。

今回は前回の続きを行っていきます。

が!その前に、現在作っているEA(自動売買プログラム)の仕様を確認しておきましょう。↓

前回は、ArraySetAsSeries関数CopyBuffer関数 を使い、単純移動平均(SMA)に関する値を配列に格納するところまで完了しました。

次は終値情報を取得します。今回作るEA(自動売買プログラム)は現在値と単純移動平均(SMA)との位置関係で、売買判断をしていきますから終値情報を取得しないことにはEA(自動売買プログラム)が作れません。

スポンサーリンク
スポンサーリンク

終値情報を取得する

終値情報を取得するにはiClose関数か、CopyClose関数を使います。

iClose関数は記述が簡単というメリットがある一方で、ある1点の時点の終値情報しか取得できません。

CopyClose関数配列を別途用意したり、ArraySetAsSeries関数を併用する必要がある一方で、配列格納後は複数の終値情報を扱うことができます。

iClose関数CopyClose関数両方の概要を見ていきましょう。

iClose関数について

iClose関数引数で指定したチャート期間・シフト位置における終値を返す関数です。

iClose関数引数戻り値の構成は以下のようになっています。

double  iClose( 
   const string        symbol,         // 銘柄 
  ENUM_TIMEFRAMES     timeframe,      // 期間 
  int                shift           // シフト 
  );

戻り値は、指定したチャート期間・シフト位置における終値

第1引数には銘柄を指定する

第1引数には銘柄を指定します定義済み変数_Symbol」、あるいはSymbol関数を記述すると、プログラムを挿入したチャートの銘柄を指定したことになります。

Symbol関数についての詳細は↓の記事をご参照ください

第2引数には時間軸を指定する

第2引数には時間軸を指定します。定数ENUM_TIMEFRAMESで定められた値を設定します。0、もしくはPERIOD_CURRENTと記述すると現在のチャートの時間軸が適用されます。

定義済み変数については→コチラをご覧ください。

定数については→コチラをご覧ください。

enum列挙型については→コチラをご覧ください。

第3引数には 終値を取得する位置を指定する

第3引数には 終値を取得する位置を指定します。終値のバッファデータは、最新のデータが0番に格納され、時間軸が古い方に向かって、1.2.3・・・n番目と並んでいます。最新足の終値を取得したい場合は「0」と記述します。

今回作るEA(自動売買プログラム)最新終値と最新足単純移動平均(SMA)との位置関係で売買判断を行うので、最新足終値(=現在値)が必要なのですが、iClose関数を使って取得すると↓のようになります。

//最新足の終値情報を取得する
	double close=iClose(_Symbol,0,0);

CopyClose関数について

CopyClose関数引数で指定した範囲の終値情報を取得し、その情報を配列にコピーする関数です。

CopyClose関数引数戻り値の構成は以下のようになっています。(CopyClose関数は3つの書式をもつオーバーロード関数ですが、公式リファレンスにある第1書式のみ引用します)※オーバーロード関数については→コチラをご覧ください。

int  CopyClose( 
   string          symbol_name,      // 銘柄名 
  ENUM_TIMEFRAMES  timeframe,        // 期間 
  int              start_pos,        // 開始位置 
  int              count,            // 複製するデータ数 
  double          close_array[]      // 受け取り側の配列 
  );

第1引数には銘柄を指定する

第1引数には銘柄を指定します定義済み変数_Symbol」、あるいはSymbol関数を記述すると、プログラムを挿入したチャートの銘柄を指定したことになります。

第2引数には時間軸を指定する

第2引数には時間軸を指定します。定数ENUM_TIMEFRAMESで定められた値を設定します。0、もしくはPERIOD_CURRENTと記述すると現在のチャートの時間軸が適用されます。

第3引数には、コピー開始位置を指定する

第3引数には、コピー開始位置を指定します。終値のバッファデータは、最新のデータが0番に格納され、時間軸が古い方に向かって、1.2.3・・・n番目と並んでいます。

第4引数には複製データ数を指定する

第4引数には複製データ数を指定します。第3引数で指定したコピー開始位置から指定した数だけバッファデータをコピーします。例えば第4引数に「10」と記述し、第3引数に「0」と記述した場合、最新足から10期間分のバッファデータが配列にコピーされます。

第5引数には複製データ数をコピーする配列を指定する

第5引数には複製データ数をコピーする配列を指定します。注意したいのが、第1引数から第4引数までは入力データを記述していましたが、第5引数は出力データを記述しているということです。

第1引数から第4引数までが注文伝票であり、注文した商品を第5引数で受け取るイメージです。↓

今回作るEA(自動売買プログラム)は最新終値と最新足単純移動平均(SMA)との位置関係で売買判断を行うので、最新足終値(=現在値)が必要なのですが、CopyClose関数を使って取得すると↓のようになります。

// 終値情報を格納する配列を宣言
	double close[];
//配列close[]を時系列にセット
	ArraySetAsSeries(close,true);
//終値情報を配列close[]にコピー
	CopyClose(_Symbol,0,0,1,close);

これでclose[0]と記述すれば、最新足の終値情報が取得できます。

ここまでiClose関数 と CopyClose関数両方の概要をお伝えしました。

どちらを使ってもいいのですが、今回作るEA(自動売買プログラム)の場合、必要とする情報は最新足の終値情報のみなので、事前に配列を用意したりArraySetAsSeries関数を使ってまでCopyClose関数を使う必然性もないでしょう。

簡潔さをとってiClose関数を使った記述で進めたいと思います。

ポジション状況の確認とポジション番号の取得

終値情報の取得ができたら、次はポジション状況の確認とポジション番号の取得を行います。

EA(自動売買プログラム)の仕様で、「ポジションは最大1つまで」と決めているので、売買条件の記述をする前にポジション状況の確認とポジション番号の取得に関する記述が必要になります。

買いポジションと売りポジションのポジション番号を格納する変数を宣言

まずはポジション番号を格納する為の変数を宣言します。

// ポジション状況の確認とポジション番号の取得
//買いポジションと売りポジションのチケット番号を格納する変数を宣言
   ulong buyPosNum = 0, sellPosNum = 0;

買いポジション用に「buyPosNum」、売りポジション用に「sellPosNum」を宣言しました。

ちなみにデータ型ulong型になっているのは、ポジション番号を取得する役割を担っているPositionGetTicket関数戻り値ulong型になっているからです。

どういう想定でulong型にしているのかは不明ですが、コンパイル時に警告が出るのを避ける為に、受け皿となる変数データ型ulong型に合わせた方が良いでしょう。

ulong型というのは、整数型の1種でマイナスの値は保持できない代わりに、int型よりも幅広いレンジの値を格納できるデータ型のことです。

詳しくはMQL5 EA講座 第11回補講「int以外の整数型」をご覧ください。

ポジション数を確認し、ポジションがあるようならポジション番号を取得・格納する

ポジション番号を格納する為の変数を宣言した後は、for文を使ってポジションチェックを行います。

for(int i = 0; i < PositionsTotal(); i++)
     {
      ulong ticketNum = PositionGetTicket(i);
      PositionSelectByTicket(ticketNum);

      if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
        {
         buyPosNum = ticketNum;
         BuyPosition = true;

        }//if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)

      else
         if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
           {
            sellPosNum = ticketNum;
            SellPosition = true;

           }// else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)

     }//for(int i = 0; i < PositionsTotal(); i++)

for文PositionsTotal関数を使ったポジションチェックの記述については→コチラで解説済みです。

PositionGetTicket関数で取得したポジション番号変数「ticketNum」に格納します。

その変数「ticketNum」をPositionSelectByTicket関数引数に記述することによって、PositionGetIntegerPositionGetDouble,PositionGetStringなどをつかってポジションに関する詳細情報が獲得できるようになります。

PositionGetInteger(POSITION_TYPE)によって、獲得できた値が定数値「POSITION_TYPE_BUY」だった場合、

現在買いポジションを持っていることを意味しますから、買いポジションのポジション番号を格納する目的で用意した変数buyPosNumにticketNumの値を代入します。

そして直近に買いポジションを保有していたフラグを立てる為に用意したbool型変数のBuyPositionにtrueを代入します。

変数BuyPositionはポジションを持っている時だけではなく、直近のポジションが買いポジションだったかどうかも確認する役割を担っています。

反対に、

PositionGetInteger(POSITION_TYPE)によって、獲得できた値が定数値「POSITION_TYPE_SELL」だった場合、

売りポジションを持っていることを意味しますから、売りポジションのポジション番号を格納する目的で用意した変数sellPosNumにticketNumの値を代入します。

そして直近に売りポジションを持っているフラグを立てる為に用意したbool型変数のSellPositionにtrueを代入します。

変数SellPositionはポジションを持っている時だけではなく、直近のポジションが売りポジションだったかどうかも確認する役割を担っています。

グローバル変数については→コチラをご覧ください。

ローカル変数については→コチラをご覧ください。

if文については→コチラをご覧ください。

else文については→コチラをご覧ください。

エントリー条件を記述する(買いエントリー)

ポジション情報のチェックと確認が終わったら、次はエントリー条件を記述していきます。

買いのエントリー条件を記述できれば、売りのエントリー条件も構造自体は同じで細かい要素を逆に書き換えていくだけです。

買いエントリーの条件は、

現在値がSMAを上回っていて、ポジションが0の時に買いエントリーする。

という仕様に決定しているので、それを踏まえて記述していきます。

 if(close > ma[0] && BuyPosition== false)
   {
   }

↑の記述で、まずは

現在値が単純移動平均(SMA)を上回って、直近のポジションが買いポジションじゃなかった場合」という条件式になりました。

これで売りポジションを持っていなければ、OrderSend関数を使って買い注文を出したいところです。

ですが!少し待ってください。

BuyPosition== falseの時は、SellPosition==trueに、

BuyPosition== trueの時は、SellPosition==falseになるようにコードを記述していくので、

if(close > ma[0] && BuyPosition== false)

の条件式が通っている時は、SellPosition==trueにはなっているはずなのですが、現在ポジションを持っているかどうかまでは判断できません。

直近の保有ポジションは売りポジションだったけれど、ストップロスやテイクプロフィットにかかってもうポジション自体は持っていない可能性もあります。

今回作るEA(自動売買プログラム)では、買いエントリー条件を満たした時点で売りポジションを持っていた場合は、売りポジションを決済する回路を組み込みます(いわゆるドテン売買ですが、ストップロスとテイクプロフィットも設定しているので完全ドテン売買EAではないです)

買いエントリーの条件を満たしている時に、売りポジションを持っていれば売りポジションを決済する

注目すべきMqlTradeRequest構造体 の各メンバ変数の設定について軽く触れると・・・↓

決済注文時も新規注文時と同様.action に設定するのはTRADE_ACTION_DEALです

売りポジションを決済するので、.type に設定するのは必然的に ORDER_TYPE_BUY となります。

ポジション番号を格納するメンバ変数 .position には、売りポジションのポジション番号が格納されている変数 sellPosNumを代入します。(「ポジション状況の確認とポジション番号の取得」の所で取得の流れは説明しています。

混乱してきてしまった方は、戻って見直してみてください)

// 買いの注文に関する記述
   if(close> sma[0] && BuyPosition == false)
     {
      // 売りポジションがあれば決済する。
      if(sellPosNum > 0)
        {
         PositionSelectByTicket(sellPosNum);

         request.action = TRADE_ACTION_DEAL;
         request.type = ORDER_TYPE_BUY;
         request.symbol = _Symbol;
         request.position = sellPosNum;
         request.volume = PositionGetDouble(POSITION_VOLUME);
         request.price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
         request.deviation = Deviation;

         bool sent = OrderSend(request, result);
        }

その他、言及していないMqlTradeRequest構造体各メンバ変数 の設定については

第64回「ポジションをクローズする」 ←を参考にしてください。

これで、OrderSend関数にて決済注文が出され、売りポジションがなくなりました。

※ちなみに、

bool sent = OrderSend(request, result);

と、

OrderSend関数戻り値bool型のsentという変数が格納していますが、コンパイル時の「return value of ‘OrderSend’ should be checked (OrderSend関数戻り値をチェックしましょう)」という警告を出さないためのもので、基本的に他に使う事はありません。

この戻り値を使う事はないので、警告が出たままでも影響はないのですが、システムの堅牢性を保つために、なるべく日頃から警告が出たら修正する癖は付けた方が良いでしょう↓

MQL4のOrderSend関数戻り値としてチケット番号を返しますが、MQL5OrderSend関数戻り値「トレードサーバーの受入可否」です。

trueが返ってきたとしても、あくまでトレードサーバーが「その注文受けつけました」という意味であり、注文成功を意味するものではありません。

注文成功可否はあくまで次回解説するMqlTradeResult構造リターンコードで確認します。 

公式リファレンスの注文結果定数値一覧

.retcode 

これで、OrderSend関数にて決済注文が出され、売りポジションがなくなりました。

次は買い注文の記述に入ります。

買いエントリーに必要なメンバ変数を設定していく

   // 買い注文を出す
      request.action = TRADE_ACTION_DEAL;
      request.type = ORDER_TYPE_BUY;
      request.symbol = _Symbol;
      request.position = 0;
      request.volume = Volume;
      request.price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
      request.sl = 0;
      request.tp = 0;
      request.deviation = Deviation;

      bool sent = OrderSend(request,result);

買いエントリーに必要なメンバ変数を設定していきますが、基本的にやることは

第62回「実際に注文を出すコードを書いてみる」 で解説したことをやるだけです。


    request.action=TRADE_ACTION_DEAL;
     

成行注文なので、.action にはTRADE_ACTION_DEALを設定します。

 request.type=ORDER_TYPE_BUY;
      

買い注文なので、.type にはORDER_TYPE_BUYを設定します。

request.symbol=_Symbol;

取引銘柄はプログラムを実行するチャートの銘柄を指定したいので、.symbolには定義済み変数_Symbolを設定します。

      request.volume=Volume;

ロット数はインプット変数「Volume」をメンバ変数 .Volume代入します。

request.price=SymbolInfoDouble(_Symbol,SYMBOL_ASK);

成行の買い注文なので、注文価格を設定する.Price にはAsk値が入ります。Ask値を取得するには、

SymbolInfoDouble関数を使います。第2引数に、定数SYMBOL_ASKを記述することによって取得できます。

request.sl=0;
request.tp=0;
      

ストップロス(損切り価格)とテイクプロフィット(利益確定価格)は、注文が約定したことを確認してからTRADE_ACTION_SLTPアクションによって設定しますので、.sl と.tpには0を入れておきます。

TRADE_ACTION_SLTPアクションについては、↓

第63回「約定したポジションにSLとTPを設定する」をご参照ください。

request.deviation=Deviation;

執行方式instant執行方式またはRequest執行方式の場合、スリッページを設定する必要があります。.deviationにはインプット変数「Deviation」を代入しておきます。

執行方式については↓

第58回「成行注文と執行方式について」をご参照ください。

request.magic=Magic;

マジックナンバーを設定する.magicにはinput変数「Magic」を代入します。

これで、メンバ変数の設定は完了です。

あとは、requesとresultというMqlTradeRequest構造体MqlTradeResult構造体インスタンスをそれぞれ第1引数と第2引数OrderSend関数に記述すれば買いの注文コードは完了です。

約定後にSLとTPの設定をする

注文が約定したら、ストップロスとテイクプロフィットを設定していきます。↓

  // ストップロスとテイクプロフィットを設定する
      if(result.retcode == TRADE_RETCODE_PLACED || result.retcode == TRADE_RETCODE_DONE)
        {
         request.action = TRADE_ACTION_SLTP;
         request.position = result.order;

         PositionSelectByTicket(result.order);
         double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);

         if(SL > 0)
            request.sl = positionOpenPrice - (SL * _Point);
         if(TP > 0)
            request.tp = positionOpenPrice + (TP * _Point);

         if(request.sl > 0 || request.tp > 0)
            sent = OrderSend(request,result);

         SellPosition = false;
        }
     }

第63回「約定したポジションにSLとTPを設定する」の内容に従ってSLTP設定用のメンバ変数設定を行っていきます。↓

  if(result.retcode == TRADE_RETCODE_PLACED || result.retcode == TRADE_RETCODE_DONE)

第61回「MqlTradeResult構造体について」で解説したように、.retcode に格納されているのがTRADE_RETCODE_PLACED か TRADE_RETCODE_DONEだった場合は無事に約定したことを意味するので、ストップロスとテイクプロフィットを設定する為の記述に移ります。

 Print("注文が成功しました。 リターンコード= ",result.retcode);
      request.action = TRADE_ACTION_SLTP;

SLとTPを設定したいので、.actionにはTRADE_ACTION_SLTPを設定します。

 request.position = result.order;

ヘッジングシステム口座においてはポジション番号が必要になるので、.position にはMqlTradeResult構造体メンバ変数 .order代入します。

成行注文の際は、オーダー番号ポジション番号は同じものが同時に割り振られるのでその性質を利用します。

<関連リンク>

オーダー番号とは?

ポジション番号とは?

約定番号(deal number)とは?

PositionSelectByTicket(result.order);
double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);

PositionSelectByTicket関数は、引数ポジション番号を指定することによって、指定ポジションの様々な情報を取得する作業を許可する関数です

PositionSelectByTicket関数の処理が正常に行われれば、以降PositionGetDouble関数PositionGetInteger関数PositionGetStringなどの関数を使って指定ポジションの情報取得が可能になります。

double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);

PositionGetDouble関数引数には、定数値「POSITION_PRICE_OPEN」を記述します。

取得したポジションの約定価格を、double型変数「openPrice」に格納し、SLとTPを設定する前準備を整えます。

if(SL > 0) request.sl = positionOpenPrice - (SL * _Point);
if(TP > 0) request.tp = positionOpenPrice + (TP * _Point);

if文で、変数TP、SLそれぞれに、0より大きい数値が入っているかを切り分けさせたうえで、メンバ変数 .sl と.tp にそれぞれストップロスとテイクプロフィットの値を代入していきます

定義済み変数_Point についてはコチラ

if(request.sl > 0 || request.tp > 0)
            sent = OrderSend(request,result);

最後に.sl と.tpどちらかに、0以上の数値が入っているか切り分けたうえでOrderSend関数で注文を出します。

SellPosition = false;

買い注文の約定~ストップロスとテイクプロフィット設定まで完了したので、ここで直近保有ポジションがSellだったことを示す変数「SellPosition」 をfalseにして、全ての買い注文工程は終わりです。

売り注文はこれを逆にしていくだけです。

エントリー条件を記述する(売りエントリー)

売りエントリーの条件は、

現在値がSMAを下回っていて、ポジションが0の時に売りエントリーする

という仕様なので、買いエントリー記述の要所要所を逆にしていく作業になります。

 if(close <ma[0] && SellPosition== false)
   {
   }

売りエントリー条件を満たしたら、買いポジションを持っていないかどうかチェックして、持っていたらその買いポジションは決済する。

エントリー条件を満たしたら、即座に新規注文を出すのではなく、買いポジションを持っていないかチェックし、買いポジションを持っていたら決済します。

 if(close <sma[0] && SellPosition== false)
   {
        // 買いポジションがあれば決済する。
      if(BuyPosNum > 0)
        {
         PositionSelectByTicket(BuyPosNum);

         request.action = TRADE_ACTION_DEAL;
         request.type = ORDER_TYPE_SELL;
         request.symbol = _Symbol;
         request.position = BuyPosNum;
         request.volume = PositionGetDouble(POSITION_VOLUME);
         request.price = SymbolInfoDouble(_Symbol,SYMBOL_BID);
         request.deviation = Deviation;

         bool sent = OrderSend(request, result);
        }
   }

.action →TRADE_ACTION_DEAL ここは同じですね。

買いポジションを決済するので、.type にはORDER_TYPE_SELL代入します。

.position には、買いポジションのポジション番号が格納されている変数 「BuyPosNum」を代入します。

.Priceには現在のBid値を入れます。Bid値の取得にはSymbolInfoDouble関数の第2引数に SYMBOL_BID を記述します。

決済用のメンバ変数設定が終わったらOrderSend関数で決済注文を出します。

反対ポジションの決済が終わったら新規売りポジションの記述に入ります

売りエントリーに必要なメンバ変数を設定していく

 // 売り注文を出す
      request.action = TRADE_ACTION_DEAL;
      request.type = ORDER_TYPE_SELL;
      request.symbol = _Symbol;
      request.position = 0;
      request.volume = Volume;
      request.price = SymbolInfoDouble(_Symbol,SYMBOL_BID);
      request.sl = 0;
      request.tp = 0;
      request.deviation = Deviation;

      bool sent = OrderSend(request,result);

買いエントリーに必要なメンバ変数を設定していく」の記述とほとんど同じです。

変更しているのは、

.typeORDER_TYPE_SELLになっているのと、

.Price代入するSymbolInfoDouble関数の第2引数が SYMBOL_BID になっている

ぐらいです。

新規売り注文に必要なメンバ変数設定が終わったらOrderSend関数で新規売り注文を出します。

次はストップロスとテイクプロフィットの設定です。

約定後にSLとTPの設定をする

注文が約定したら、ストップロスとテイクプロフィットを設定していきます。↓

 // ストップロスとテイクプロフィットを設定する
      if(result.retcode == TRADE_RETCODE_PLACED || result.retcode == TRADE_RETCODE_DONE)
        {
         request.action = TRADE_ACTION_SLTP;
         request.position = result.order;

         PositionSelectByTicket(result.order);
         double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);

         if(SL > 0)
            request.sl = positionOpenPrice - (SL * _Point);
         if(TP > 0)
            request.tp = positionOpenPrice + (TP * _Point);

         if(request.sl > 0 || request.tp > 0)
            sent = OrderSend(request,result);

         SellPosition = false;
        }
     }

ストップロスとテイクプロフィットの計算式の記号も逆になります

.sl  に代入する計算式は→ positionOpenPrice + (StopLoss * _Point) となり、

.tp代入する計算式はpositionOpenPrice – (TP * _Point) になります。

最後に.sl と.tpどちらかに、0以上の数値が入っているか切り分けたうえでOrderSend関数で注文を出します。

売り注文の約定~ストップロスとテイクプロフィット設定まで完了したので、ここで直近保有ポジションがSellだったことを示す変数BuyPosition をfalseにして、全ての売り注文工程は終わりです。

完成したコード全体の記述内容

完成したコード全体の記述内容は以下の通りです↓

//+------------------------------------------------------------------+
//|                                                      testEA1.mq5 |
//|                                                         MQL5ssei |
//|                                    https://mqlinvestmentlab.com/ |
//+------------------------------------------------------------------+
#property copyright "MQL5ssei"
#property link      "https://mqlinvestmentlab.com/"
#property version   "1.00"


//必要なパラメータ
input double Volume=0.1;//ロット数
input int SL=200;//ストップロス
input int TP=200;//テイクプロフィット
input int MAPeriod = 20;//移動平均期間
input int Deviation=50;//スリッページ
input int Magic=1234;//マジックナンバー

//直近の注文状況をチェックするグローバル変数
bool BuyPosition, SellPosition;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {

   ENUM_ACCOUNT_TRADE_MODE tradeMode=(ENUM_ACCOUNT_TRADE_MODE)AccountInfoInteger(ACCOUNT_TRADE_MODE);
   if(tradeMode != ACCOUNT_TRADE_MODE_DEMO)                 // デモ口座以外の場合
     {

      MessageBox("このEAはデモ口座でのみ稼働します","口座エラー");
      return INIT_FAILED;                            // 処理終了
     }


//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
  //インスタンスの生成
   MqlTradeRequest request;
   MqlTradeResult result;
   
   //構造体の初期化
   ZeroMemory(request);
  // ZeroMemory(result);

   //フィルポリシーを設定する。
   request.type_filling=FillPolicy();

// 単純移動平均の値取得
//単純移動平均の値を格納する配列宣言
   double sma[];

//配列を時系列にセット
   ArraySetAsSeries(sma,true);

//単純移動平均のハンドルを取得
   int smaHandle=iMA(_Symbol,0,MAPeriod,MODE_SMA,0,PRICE_CLOSE);
///単純移動平均の値を配列にコピー
   CopyBuffer(smaHandle,0,0,1,sma);


	//終値情報を変数に格納
	double close=iClose(NULL,0,0);
   
// ポジション状況の確認とポジション番号の取得
//買いポジションと売りポジションのチケット番号を格納する変数を宣言
   ulong buyPosNum = 0, sellPosNum = 0;
   for(int i = 0; i < PositionsTotal(); i++)
     {
      ulong ticketNum = PositionGetTicket(i);
      PositionSelectByTicket(ticketNum);

      if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
        {
         buyPosNum = ticketNum;
         BuyPosition = true;

        }//if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)

      else
         if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
           {
            sellPosNum = ticketNum;
            SellPosition = true;

           }// else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)

     }//for(int i = 0; i < PositionsTotal(); i++)

// 買いの注文に関する記述
   if(close> sma[0] && BuyPosition == false)
     {
      // 売りポジションがあれば決済する。
      if(sellPosNum > 0)
        {
         PositionSelectByTicket(sellPosNum);

         request.action = TRADE_ACTION_DEAL;
         request.type = ORDER_TYPE_BUY;
         request.symbol = _Symbol;
         request.position = sellPosNum;
         request.volume = PositionGetDouble(POSITION_VOLUME);
         request.price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
         request.deviation = Deviation;

         bool sent = OrderSend(request, result);
        }

      // 買い注文を出す
      request.action = TRADE_ACTION_DEAL;
      request.type = ORDER_TYPE_BUY;
      request.symbol = _Symbol;
      request.position = 0;
      request.volume = Volume;
      request.price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
      request.sl = 0;
      request.tp = 0;
      request.deviation = Deviation;

      bool sent = OrderSend(request,result);

      // ストップロスとテイクプロフィットを設定する
      if(result.retcode == TRADE_RETCODE_PLACED || result.retcode == TRADE_RETCODE_DONE)
        {
         request.action = TRADE_ACTION_SLTP;
         request.position = result.order;

         PositionSelectByTicket(result.order);
         double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);

         if(SL > 0)
            request.sl = positionOpenPrice - (SL * _Point);
         if(TP > 0)
            request.tp = positionOpenPrice + (TP * _Point);

         if(request.sl > 0 || request.tp > 0)
            sent = OrderSend(request,result);

         SellPosition = false;
        }
     }


    //売り注文に関する記述
      if(close <sma[0] && SellPosition== false)
   {
        // 買いポジションがあれば決済する。
      if(buyPosNum > 0)
        {
         PositionSelectByTicket(buyPosNum);

         request.action = TRADE_ACTION_DEAL;
         request.type = ORDER_TYPE_SELL;
         request.symbol = _Symbol;
         request.position = buyPosNum;
         request.volume = PositionGetDouble(POSITION_VOLUME);
         request.price = SymbolInfoDouble(_Symbol,SYMBOL_BID);
         request.deviation = Deviation;

         bool sent = OrderSend(request, result);
        }//if(buyPosNum > 0)
        
         // 売り注文を出す
      request.action = TRADE_ACTION_DEAL;
      request.type = ORDER_TYPE_SELL;
      request.symbol = _Symbol;
      request.position = 0;
      request.volume = Volume;
      request.price = SymbolInfoDouble(_Symbol,SYMBOL_BID);
      request.sl = 0;
      request.tp = 0;
      request.deviation = Deviation;

      bool sent = OrderSend(request,result);
      
      // ストップロスとテイクプロフィットを設定する
      if(result.retcode == TRADE_RETCODE_PLACED || result.retcode == TRADE_RETCODE_DONE)
        {
         request.action = TRADE_ACTION_SLTP;
         request.position = result.order;

         PositionSelectByTicket(result.order);
         double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);

         if(SL > 0)
            request.sl = positionOpenPrice + (SL * _Point);
         if(TP > 0)
            request.tp = positionOpenPrice - (TP * _Point);

         if(request.sl > 0 || request.tp > 0)
            sent = OrderSend(request,result);

         BuyPosition = false;
        }
        
   }// if(close <ma[0] && SellPosition== false)


  }//OnTick
  
//+------------------------------------------------------------------+
//フィルポリシーを返す関数
ENUM_ORDER_TYPE_FILLING FillPolicy()
{
   long fillType = SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE);
   if(fillType==SYMBOL_FILLING_IOC)return ORDER_FILLING_IOC;
   else if(fillType==SYMBOL_FILLING_FOK)return ORDER_FILLING_FOK;
   else return ORDER_FILLING_RETURN;

}

完成したEAの挙動をMT5で行ってみると以下のようになります。

まとめ

今回は前回に引き続き、シンプルEA(自動売買プログラム)のコード解説を行いました。

とりあえずEA(自動売買プログラム)を完成させましたが、あくまで作り方の一例を見て頂いただけで、利益を出すことを目的としてたものではありません。

デモ口座でしか動かない記述も入れていますが、くれぐれもリアル口座で運用するような事は止めてくださいね。

※また、今後EAをつくっていく工程を解説する講座記事としては以下のようなものを予定しています。

MQL5 EA講座 第92回「待機注文クラスを使ってEAを作る」

今回は以上になります。

最後までお読みいただきありがとうございました<m(__)m>

【超入門】MQL5 EA講座 第68回「簡単な仕組みのMT5用EAを作るーその1ー」【EAの作り方】

     →MQL5 EA講座 第70回「簡単な仕組みのEAを作るーネッティングシステムの-場合ー」

コメント

タイトルとURLをコピーしました