【MQL4】iCCI関数の使い方を徹底解説!CCI(商品チャネル指数)でトレンド転換を捉えるEAを作ろう

【中級編】MQLプログラムの読み方・書き方

CCI(商品チャネル指数)は、相場の「買われすぎ・売られすぎ」を数値で判断できる人気のオシレーター系インジケーターです。MQL4ではiCCI関数を使うことで、EA(自動売買)の中から簡単にCCIの値を取得できます。

この記事では、iCCI関数の基本的な使い方からパラメータの意味、そしてCCIのクロスを利用した実践的なEAの作成方法まで、ステップバイステップで解説していきます。

CCI(商品チャネル指数)とは?

CCIは1980年にドナルド・ランバート氏が開発したモメンタム系オシレーターです。もともとは商品先物市場向けに作られましたが、現在ではFXや株式など幅広い市場で使われています。

CCIの特徴は、RSIやストキャスティクスのように0〜100の範囲に収まるのではなく、上下に制限なく動く点です。一般的には以下の基準で判断します。

  • +100を上回る:買われすぎ(上昇トレンドが強い)
  • -100を下回る:売られすぎ(下降トレンドが強い)
  • 0ライン:トレンドの方向転換の目安

CCIの計算式

CCIの計算式は以下の通りです。

CCI = (TP - MA) / (0.015 × MD)

// TP(ティピカルプライス)= (高値 + 安値 + 終値) / 3
// MA = TPの単純移動平均(期間N)
// MD = TPと移動平均の平均偏差
// 0.015 = CCI値の約70〜80%が±100以内に収まるように設定された定数

計算式を覚える必要はありませんが、TPをベースにしていることは頭に入れておきましょう。iCCI関数のapplied_priceパラメータに関わってきます。

iCCI関数の構文

MQL4でCCIの値を取得するには、iCCI関数を使います。構文は以下の通りです。

double iCCI(
   string       symbol,          // 通貨ペア名
   int          timeframe,       // 時間足
   int          period,          // 平均期間
   int          applied_price,   // 適用価格
   int          shift            // シフト(何本前の足か)
);

パラメータ一覧

パラメータ 説明
symbol string 通貨ペア名。現在のチャートならNULLまたはSymbol()
timeframe int 時間足。0で現在のチャートの時間足。PERIOD_H1など定数も使用可能
period int CCIの計算期間。一般的には1420がよく使われる
applied_price int 計算に使う価格の種類
shift int 何本前のローソク足のCCI値を取得するか。0が現在の足、1が1本前の確定足

applied_price(適用価格)の定数一覧

CCIの計算に使用する価格タイプを指定します。デフォルトのCCIではティピカルプライスが使われます。

定数名 説明
PRICE_CLOSE 0 終値
PRICE_OPEN 1 始値
PRICE_HIGH 2 高値
PRICE_LOW 3 安値
PRICE_MEDIAN 4 中央値(高値+安値)/ 2
PRICE_TYPICAL 5 ティピカルプライス(高値+安値+終値)/ 3
PRICE_WEIGHTED 6 加重終値(高値+安値+終値×2)/ 4

チャート上にCCIインジケーターを表示して比較する場合は、applied_priceを同じ設定にしないと値がずれるので注意してください。標準のCCIはPRICE_TYPICALを使用しています。

基本的な使い方

まずは最もシンプルな使い方を見てみましょう。1本前の確定足のCCI値を取得してコメントに表示する例です。

void OnTick()
{
   // 1本前の確定足のCCI(期間14、ティピカルプライス)を取得
   double cciValue = iCCI(NULL, 0, 14, PRICE_TYPICAL, 1);
   
   // チャート上にCCI値を表示
   Comment("CCI(14) = ", DoubleToString(cciValue, 2));
}

ポイント:shiftに1を指定して確定足の値を使うのが基本です。0(現在の足)はティックごとに値が変動するため、売買シグナルの判定には向きません。

CCIクロスで売買するEAを作ろう

ここからが本番です。CCIが-100を下から上にクロスしたら買い+100を上から下にクロスしたら売り0ラインクロスで決済するEAを作成します。

トレードロジックの整理

  • 買いエントリー:CCI(1本前)が-100以上 かつ CCI(2本前)が-100未満 → -100を上抜け
  • 売りエントリー:CCI(1本前)が+100以下 かつ CCI(2本前)が+100超 → +100を下抜け
  • 買いポジション決済:CCIが0を下回った時
  • 売りポジション決済:CCIが0を上回った時

完全なEAコード

//+------------------------------------------------------------------+
//| CCI Cross EA                                                       |
//+------------------------------------------------------------------+
#property strict

// 入力パラメータ
input int    CCI_Period      = 14;            // CCI期間
input double LotSize         = 0.1;           // ロットサイズ
input int    StopLoss        = 100;           // ストップロス(pips)
input int    TakeProfit      = 200;           // テイクプロフィット(pips)
input int    MagicNumber     = 20240101;      // マジックナンバー

//+------------------------------------------------------------------+
//| Expert tick function                                               |
//+------------------------------------------------------------------+
void OnTick()
{
   // CCI値を取得(確定足を使用)
   double cci1 = iCCI(NULL, 0, CCI_Period, PRICE_TYPICAL, 1); // 1本前
   double cci2 = iCCI(NULL, 0, CCI_Period, PRICE_TYPICAL, 2); // 2本前
   
   // 現在のポジション状態を確認
   int buyCount  = CountPositions(OP_BUY);
   int sellCount = CountPositions(OP_SELL);
   
   // --- 決済ロジック ---
   // 買いポジション保有中:CCIが0を下回ったら決済
   if(buyCount > 0 && cci1 < 0)
   {
      ClosePositions(OP_BUY);
   }
   
   // 売りポジション保有中:CCIが0を上回ったら決済
   if(sellCount > 0 && cci1 > 0)
   {
      ClosePositions(OP_SELL);
   }
   
   // --- エントリーロジック ---
   // ポジションがない場合のみエントリー
   if(buyCount == 0 && sellCount == 0)
   {
      // 買いシグナル:CCIが-100を下から上にクロス
      if(cci2 < -100 && cci1 >= -100)
      {
         double sl = (StopLoss > 0)   ? Ask - StopLoss * Point * 10 : 0;
         double tp = (TakeProfit > 0)  ? Ask + TakeProfit * Point * 10 : 0;
         
         int ticket = OrderSend(Symbol(), OP_BUY, LotSize, Ask, 30, sl, tp,
                                "CCI Buy", MagicNumber, 0, clrBlue);
         if(ticket < 0)
            Print("Buy OrderSend error: ", GetLastError());
      }
      
      // 売りシグナル:CCIが+100を上から下にクロス
      if(cci2 > 100 && cci1 <= 100)
      {
         double sl = (StopLoss > 0)   ? Bid + StopLoss * Point * 10 : 0;
         double tp = (TakeProfit > 0)  ? Bid - TakeProfit * Point * 10 : 0;
         
         int ticket = OrderSend(Symbol(), OP_SELL, LotSize, Bid, 30, sl, tp,
                                "CCI Sell", MagicNumber, 0, clrRed);
         if(ticket < 0)
            Print("Sell OrderSend error: ", GetLastError());
      }
   }
}

//+------------------------------------------------------------------+
//| 指定タイプのポジション数をカウント                                    |
//+------------------------------------------------------------------+
int CountPositions(int type)
{
   int count = 0;
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == type)
            count++;
      }
   }
   return count;
}

//+------------------------------------------------------------------+
//| 指定タイプのポジションを全て決済                                      |
//+------------------------------------------------------------------+
void ClosePositions(int type)
{
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == type)
         {
            double closePrice = (type == OP_BUY) ? Bid : Ask;
            if(!OrderClose(OrderTicket(), OrderLots(), closePrice, 30, clrYellow))
               Print("OrderClose error: ", GetLastError());
         }
      }
   }
}

コードのポイント解説

① MagicNumberの活用

MagicNumberを使うことで、このEAが出した注文だけを識別できます。手動注文や他のEAの注文を誤って決済してしまう事故を防げます。

② 確定足(shift=1, 2)でクロスを検出

2本前と1本前の確定足を比較してクロスを検出しています。現在の足(shift=0)を使うと、ティックごとに値が変わるためシグナルが不安定になります。

③ 決済を逆方向ではなく0ラインで行う

CCIが0ラインをクロスした時点で決済することで、利益の取りこぼしを減らす設計になっています。もちろん、SL/TPによる自動決済も併用しています。

応用テクニック:マルチタイムフレーム分析

iCCI関数の第2引数(timeframe)を変えるだけで、上位足のCCIをフィルターとして使うことができます。例えば、日足のCCIの方向に合わせてエントリーを絞り込む手法です。

// 上位足(日足)のCCIでトレンド方向を判定
double cciDaily = iCCI(NULL, PERIOD_D1, 14, PRICE_TYPICAL, 1);

// 現在の時間足のCCIでエントリータイミングを判定
double cciCurrent1 = iCCI(NULL, 0, 14, PRICE_TYPICAL, 1);
double cciCurrent2 = iCCI(NULL, 0, 14, PRICE_TYPICAL, 2);

// 日足CCIが0以上(上昇傾向)の時だけ買いエントリー
if(cciDaily > 0 && cciCurrent2 < -100 && cciCurrent1 >= -100)
{
   // 買いエントリー処理
}

// 日足CCIが0以下(下降傾向)の時だけ売りエントリー
if(cciDaily < 0 && cciCurrent2 > 100 && cciCurrent1 <= 100)
{
   // 売りエントリー処理
}

上位足のトレンド方向に逆らわないことで、ダマシのシグナルを大幅に減らすことが期待できます。

iCCI関数を使う際の注意点

1. applied_priceの不一致に注意

チャート上に表示しているCCIインジケーターと、iCCI関数で指定したapplied_priceが異なると、値がずれます。検証時は必ず同じ設定にしましょう。標準のCCIはPRICE_TYPICALです。

2. レンジ相場での連続シグナルに注意

CCIはモメンタム指標なので、レンジ相場では±100ラインを頻繁に行き来し、ダマシが多発します。移動平均線やADXなどのトレンドフィルターを組み合わせることをおすすめします。

3. 計算に必要なバー数

CCIの計算には、指定した期間分のローソク足データが必要です。チャートのバー数が少ないと正確な値が得られません。バックテスト時は十分なヒストリカルデータを用意してください。

まとめ

今回はMQL4のiCCI関数の使い方を、基本から実践的なEA作成まで解説しました。

  • iCCI関数は5つのパラメータでCCIの値を簡単に取得できる
  • ±100ラインのクロスをエントリーシグナルに、0ラインクロスを決済シグナルに使うのが王道的な手法
  • 確定足(shift=1以上)を使ってシグナル判定するのが安定した運用のコツ
  • マルチタイムフレーム分析で上位足のトレンド方向にフィルターをかけると精度が向上する
  • applied_priceはチャート上のインジケーターと合わせることを忘れずに

CCIはシンプルながら奥が深いインジケーターです。まずはサンプルEAをストラテジーテスターで動かしてみて、パラメータを変えながらCCIの振る舞いを体感してみてください!