日足EAの組み立て方

第3回で見たとおり、売買判断は OnTick に書きます。ただし OnTick は価格が動くたびに何度も呼ばれるので、そのまま書くと1日に何回も買い判定をしてしまいます。 月末ロングは1日1回の判断で十分なので、「新しい日足が確定したときだけ処理する」という仕掛けを最初に入れます。これが日足EAの定番の組み立てです。

新しい日足を検出する

新しい日足かどうかは、今の日足の時刻を覚えておき、前回と変わったら「新しい足だ」と判断することで分かります。iTime(_Symbol, PERIOD_D1, 0) は「今の日足の開始時刻」を返すので、これを記録した値と比べます。

MQL5:新しい日足の検出
datetime lastBarTime = 0;   // 前回処理した日足の時刻を覚えておく箱

void OnTick()
{
   datetime curBar = iTime(_Symbol, PERIOD_D1, 0);  // 今の日足の時刻
   if(curBar == lastBarTime) return;   // 同じ足なら何もしないで戻る
   lastBarTime = curBar;               // 新しい足なので記録を更新
   // …ここから下は、1日1回だけ動く…
}

return; は「ここで処理を打ち切って戻る」命令。同じ足のあいだは return で抜けるので、下の売買判定は1日1回しか動きません。

新しい日足 条件判定 発注/見送り 同じ足で何度も発注しない設計にする
日足EAでは、新しい足を検出してから1回だけ条件を判定します。

EAの全コード

新しい足の検出に、第3回の日付取り出し・if文・売買命令を足すと、EAはこれで完成です。全体でもこの短さです。

MQL5:月末ロングEA(全コード)
//+------------------------------------------------------------------+
//| 月末ロング(簡易 Turn-of-Month): S&P500の日足で使う教育用EA    |
//+------------------------------------------------------------------+
#include <Trade/Trade.mqh>   // 取引の道具を読み込む
CTrade trade;               // trade という名前で使えるようにする

datetime lastBarTime = 0;   // 前回処理した日足の時刻

void OnTick()
{
   // 新しい日足が確定したときだけ、1日1回判定する
   datetime curBar = iTime(_Symbol, PERIOD_D1, 0);
   if(curBar == lastBarTime) return;
   lastBarTime = curBar;

   // 直近で確定した足(1本前)の日付を取り出す
   MqlDateTime dt;
   TimeToStruct(iTime(_Symbol, PERIOD_D1, 1), dt);
   int dom = dt.day;

   bool hasPos = PositionSelect(_Symbol);   // 今ポジションを持っているか

   if(!hasPos && dom >= 25)              // ノーポジ かつ 日付25以上 → 買い
      trade.Buy(1.0);
   else if(hasPos && dom >= 3 && dom <= 15)  // 保有中 かつ 日付3〜15 → 決済
      trade.PositionClose(_Symbol);
}

1行ずつ読む

ルールがコードのどこに対応するかを確認します。

24日25日28日 31日1日2日 3日 前月 翌月 買い 25日以上・ノーポジ 保有 決済 3〜15日
月末(25日以降)に買い、翌月の3〜15日で決済。月に1度、数営業日だけ持つ戦略

ストラテジーテスターで動かす

第2回のストラテジーテスターに、このEAをかけます。設定は次のようにします。

  1. このコードを MetaEditor で .mq5 として保存し、コンパイルします(エラーが出たら直します)。
  2. ストラテジーテスターで、このEA・銘柄はS&P500系(業者により名称が異なる)・時間足はD1(日足)を選びます。
  3. 期間は、データがある範囲でできるだけ長く(10年前後)。
  4. スプレッドは、0ではなく現実的な値にします。指数CFDは手数料・スプレッドが結果に効きます。
  5. 実行し、損益・勝率・最大ドローダウン・トレード数を記録します。この数字を、第10回でPine・Pythonと突き合わせます。

この実装の約束ごと

「前の足で判定 → 次の足で約定」になっている

このEAは、確定した1本前の足の日付で判定し、新しい足に入った瞬間(その足のはじめ)に trade.Buy() を出します。つまり「昨日の引けで条件を確認し、今日の頭で約定」という動きです。 これは先読みを避けるための正しい作りですが、約定のタイミングを「足の頭」に固定しているとも言えます。第9回のPythonでは、あえて「その足の引けで約定」として計算します。同じルールなのに約定の瞬間が1本ぶんずれます。この違いが、第10回で見る結果のズレの一因になります。今はEAが正しく1日1回・先読みなしで動くことを確認できれば十分です。

チェックリスト

次の記事に進む前に、以下が自分の言葉で説明できるか確認してください。

ミニテスト

最後に3問だけ確認です。不正解なら別の選択肢を選び直せます。正解すると解説が表示されます。

Q1. OnTickの先頭で「新しい日足のときだけ処理する」仕掛けを入れるのはなぜか?

OnTickは値が動くたびに何度も呼ばれます。新しい足の検出で1日1回に絞らないと、同じ日に何度も売買判定をしてしまいます。

Q2. 日付の取り出しで iTime(...,1) と1本前の足を使うのはなぜか?

今の足はまだ動いている最中で、その終値などを判定に使うと「未来を知った」状態になります。確定済みの1本前を使うことで先読みを避けます。

Q3. このEAの約定タイミングは、どう説明できるか?

確定した1本前の足で判定し、新しい足に入った瞬間に発注するので「昨日の引けで確認、今日の頭で約定」です。第9回のPythonは引けで約定として計算し、ここがズレの一因になります。