前回の記事では、一目均衡表の「雲抜け」

だけを検知するインジケーターを作りましたが、

まとめで「雲抜け単体は『だまし』

も多く、三役好転のような複合条件の一部として使われることが多い」

と書きました。

一目均衡表には本来、3つの独立したシグナルが同時にそろったときだけを

最強の買いサインとする「三役好転」

という考え方があります。

  • 転換線が基準線を上抜ける(均衡表の好転)
  • 終値が雲の上にある(雲抜け、前回の記事で扱った条件)
  • 遅行スパンが実勢価格を上回っている(遅行スパンの好転)

この記事では、この3条件を同じ確定足で同時に満たしたときだけエントリーするEAを、

遅行スパンの判定でよくある実装ミスを避ける設計とあわせて解説します。

条件1・2: 転換線と基準線のクロス、雲抜け

転換線と基準線のクロスは、

`MODE_TENKANSEN`・`MODE_KIJUNSEN`で取得した値同士を、

前回の記事と同じ「1本前との比較」

で判定します。

bool TenkanCrossedAboveKijun(int shift)
  {
   double tenkanNow  = iIchimoku(NULL,0,TenkanSen,KijunSen,SenkouSpanB,MODE_TENKANSEN,shift);
   double kijunNow   = iIchimoku(NULL,0,TenkanSen,KijunSen,SenkouSpanB,MODE_KIJUNSEN,shift);
   double tenkanPrev = iIchimoku(NULL,0,TenkanSen,KijunSen,SenkouSpanB,MODE_TENKANSEN,shift+1);
   double kijunPrev  = iIchimoku(NULL,0,TenkanSen,KijunSen,SenkouSpanB,MODE_KIJUNSEN,shift+1);

   return(tenkanNow > kijunNow && tenkanPrev <= kijunPrev);
  }

雲抜けは前回の記事とまったく同じ、

先行スパンA・Bの大きい方を雲の上限として比較する方法です。

bool PriceAboveCloud(int shift)
  {
   double spanA = iIchimoku(NULL,0,TenkanSen,KijunSen,SenkouSpanB,MODE_SENKOUSPANA,shift);
   double spanB = iIchimoku(NULL,0,TenkanSen,KijunSen,SenkouSpanB,MODE_SENKOUSPANB,shift);
   double cloudTop = MathMax(spanA,spanB);

   return(iClose(NULL,0,shift) > cloudTop);
  }

条件3: 遅行スパンの判定はMODE_CHINKOUSPANを使わない

遅行スパン(チコウスパン)は「今日の終値を、

そのまま26本(`KijunSen`本)過去の位置に描画したもの」

という単純な定義の線です。

`iIchimoku()`には`MODE_CHINKOUSPAN`という

遅行スパン専用のモードもありますが、

この値は関数内部で既にシフトされた状態で返るため、

「今どのタイミングの遅行スパンを見ているのか」

の解釈を誤ると、意図と違う位置の値を比較してしまう事故が起きやすくなります。

遅行スパンの定義そのものに立ち返ると、

「`shift`本前の遅行スパンが実勢価格を上回っている」

という条件は、次の2つの終値を直接比較するだけで表現できます。

//「shift本前の終値」(=そこに描画される遅行スパンの値)が
//「shift+KijunSen本前の終値」(=その位置の実際の価格)を上回っているか
bool ChikouAbovePrice(int shift)
  {
   return(iClose(NULL,0,shift) > iClose(NULL,0,shift+KijunSen));
  }

`MODE_CHINKOUSPAN`を経由せず`iClose()`だけで完結させることで、

内部シフトの解釈を自分で考える必要がなくなり、

コードを読む人にとっても「何と何を比べているか」

が一目で分かる、という設計上の利点があります。

実際に動くコード全体

3条件が直近の確定足(`shift=1`)ですべて成立したときだけ買いエントリーする、

実際にMetaEditorでコンパイルできるEAです。

ポジション重複防止には既存記事と同じ`HasPosition()`の骨格を使っています。

//+------------------------------------------------------------------+
//|                             IchimokuSanyakuKotenEA.mq4            |
//|                     Copyright 2026, FX-EA System Project Creator |
//|                        https://creator.fx-ea-system-project.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, FX-EA System Project Creator"
#property link      "https://creator.fx-ea-system-project.com/"
#property version   "1.00"
#property strict

input int    MagicNumber   = 34567;//マジックナンバー
input double Lots          = 0.01;//ロット数
input int    TakeProfit    = 50;//利確(pips)
input int    StopLoss      = 50;//損切り(pips)
input int    Slippage      = 3;//スリッページ
input int    TenkanSen     = 9;//転換線の期間
input int    KijunSen      = 26;//基準線の期間
input int    SenkouSpanB   = 52;//先行スパンBの期間

//+------------------------------------------------------------------+
//| このEA(マジックナンバー)のポジションを保有しているか判定する      |
//+------------------------------------------------------------------+
bool HasPosition()
  {
   for(int i=OrdersTotal()-1; i>=0; i--)
     {
      if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) continue;
      if(OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber)
         return(true);
     }
   return(false);
  }

//+------------------------------------------------------------------+
//| 好転条件1: 転換線が基準線を上抜けた確定足か                        |
//+------------------------------------------------------------------+
bool TenkanCrossedAboveKijun(int shift)
  {
   double tenkanNow  = iIchimoku(NULL,0,TenkanSen,KijunSen,SenkouSpanB,MODE_TENKANSEN,shift);
   double kijunNow   = iIchimoku(NULL,0,TenkanSen,KijunSen,SenkouSpanB,MODE_KIJUNSEN,shift);
   double tenkanPrev = iIchimoku(NULL,0,TenkanSen,KijunSen,SenkouSpanB,MODE_TENKANSEN,shift+1);
   double kijunPrev  = iIchimoku(NULL,0,TenkanSen,KijunSen,SenkouSpanB,MODE_KIJUNSEN,shift+1);

   return(tenkanNow > kijunNow && tenkanPrev <= kijunPrev);
  }

//+------------------------------------------------------------------+
//| 好転条件2: 終値が雲の上にあるか                                   |
//+------------------------------------------------------------------+
bool PriceAboveCloud(int shift)
  {
   double spanA = iIchimoku(NULL,0,TenkanSen,KijunSen,SenkouSpanB,MODE_SENKOUSPANA,shift);
   double spanB = iIchimoku(NULL,0,TenkanSen,KijunSen,SenkouSpanB,MODE_SENKOUSPANB,shift);
   double cloudTop = MathMax(spanA,spanB);

   return(iClose(NULL,0,shift) > cloudTop);
  }

//+------------------------------------------------------------------+
//| 好転条件3: 遅行スパンが実勢価格を上回っているか(MODE_CHINKOUSPANは使わない) |
//+------------------------------------------------------------------+
bool ChikouAbovePrice(int shift)
  {
   return(iClose(NULL,0,shift) > iClose(NULL,0,shift+KijunSen));
  }

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(HasPosition())
      return;

   int shift = 1;//直近の確定足で判定する

   if(TenkanCrossedAboveKijun(shift) && PriceAboveCloud(shift) && ChikouAbovePrice(shift))
     {
      double sl = Bid-StopLoss*Point*10;
      double tp = Bid+TakeProfit*Point*10;
      int ticket = OrderSend(Symbol(),OP_BUY,Lots,Ask,Slippage,sl,tp,
                              "Sanyaku Koten Buy",MagicNumber,0,Blue);
      if(ticket < 0)
         Print("買い注文に失敗しました。エラーコード=",GetLastError());
     }
  }
//+------------------------------------------------------------------+
Copy

MetaEditorで実際に動かす手順

  1. MT4のMetaEditorで「ファイル」→「新規作成」→「Expert Advisor(テンプレートを使用)」を選び、名前を「IchimokuSanyakuKotenEA」にして作成する
  2. テンプレートの中身を全て削除し、上のコードを丸ごと貼り付ける
  3. F7キー(またはコンパイルボタン)でコンパイルし、「0 error(s), 0 warning(s)」になることを確認する
  4. デモ口座のチャートへドラッグ&ドロップし、ストラテジーテスターで3条件がそろった足の前後でエントリーが発生するか、MT4標準の一目均衡表インジケーターの表示と見比べて確認する

この設計の限界

このコードは、3条件が「まったく同じ確定足」

で成立したときだけをエントリー対象にしています。

実際のチャートでは、転換線と基準線のクロス・雲抜け・遅行スパンの好転が、

数本ずれたタイミングでそれぞれ成立するケースも多く、

その場合はこのコードのままだとエントリーを取りこぼします。

「同じ足で厳密に一致」ではなく「直近N本以内に3条件がすべて成立していればよい」

という、やや緩めた判定に変更することもできますが、

その場合は3つの条件それぞれについて「いつ成立したか」

を別々に記録しておく設計が必要になり、

コードはこの記事のものより複雑になります。

また、この記事では買いのみを扱っています。

売り(三役逆転)を実装する場合は、

`TenkanCrossedAboveKijun`・

`PriceAboveCloud`・`ChikouAbovePrice`の

それぞれの不等号を反転させれば同じ考え方で書けます。

意図通りに動かないときに確認すること

  • エントリーが全く発生しない: 3条件を同じ確定足でそろえる設計のため、そもそも発生頻度が低い(三役好転はもともと頻発するシグナルではない)。ストラテジーテスターの期間を長めに取って確認する
  • `ChikouAbovePrice()`が想定と違う判定になる: `shift+KijunSen`がヒストリーデータの本数を超えていないか。超えていると`iClose()`が0を返し、常に条件が成立しなくなる
  • `MODE_CHINKOUSPAN`を使った既存コードと結果が食い違う: この記事の`iClose()`直接比較は遅行スパンの定義から素直に導出した計算方法であり、`MODE_CHINKOUSPAN`の内部シフト解釈を誤って使っている側にずれがある可能性がある
  • OrderSendが失敗する: `Slippage`や証拠金、デモ口座の自動売買許可設定(「自動売買を許可する」ボタン)を確認する

まとめ

一目均衡表の「三役好転」は、

転換線と基準線のクロス・雲抜け・遅行スパンの好転という

3つの独立したシグナルが同時にそろったときだけを対象にする、

雲抜け単体より厳格な設計です。

遅行スパンの判定は`MODE_CHINKOUSPAN`の内部シフトに頼らず、

「shift本前の終値」と「shift+KijunSen本前の終値」

を直接比較するだけで、定義どおりの結果を事故なく得られます。

3条件を同じ足でそろえる設計はエントリー機会が少なくなる分、

取りこぼしを許容するかどうかは、

この記事の限界を踏まえて読者自身で判断してください。

関連記事