English Deutsch
preview
プライスアクション分析ツールキットの開発(第30回):コモディティチャンネル指数(CCI)、Zero Line EA

プライスアクション分析ツールキットの開発(第30回):コモディティチャンネル指数(CCI)、Zero Line EA

MetaTrader 5トレーディングシステム |
128 0
Christian Benjamin
Christian Benjamin

はじめに

本記事では、MQL5を活用し、プライスアクショントレードを効率化するツールを構築します。本システムは、デュアルCCI(商品チャネル指数)、34期間のEMA(指数平滑移動平均、Exponential Moving Average)、ATR (Average True Range)、および純粋なプライスアクションという4つの要素に基づいた戦略を自動化します。各インジケーターを確認し、取引戦略全体を説明したのち、エキスパートアドバイザー(EA)の開発手順、テスト、結果分析、そして考察までを段階的に解説します。記事の最後には、これまでに開発した他のツールの概要を一覧表として掲載しています。

以下の目次をご確認ください。


インジケーターの概要

CCI(商品チャネル指数)

CCIは、現在の価格とその直近の平均値を比較し、買われ過ぎや売られ過ぎ、トレンド転換の可能性を示すモメンタムオシレーターです。 CCIは次の式で算出されます。

CI = (標準価格 − SMA) / (0.015 × 平均偏差)CCI - コモディティチャネル指数

標準価格(TP):期間中の高値・安値・終値を合計し、それを3で割って求めます。

           h + l + c  
TP =       ---------
               3

SMA(単純移動平均、Simple Moving Average)
SMAは、指定期間(例:直近20期間)のTPを平均化することで求められます。これにより日々の価格変動を平滑化し、基調トレンドを把握しやすくします。

平均偏差
各TPがSMAからどの程度離れているかを示す絶対偏差の平均値です。

この統計値は、期間中における価格の変動性(ボラティリティ)を定量的に示す指標となります。

定数係数0.015

定数係数0.015を掛けることで、CCIの値をおおむね−100から+100の範囲に収め、過熱状態を視覚的に判断しやすくしています。

CCIはDonald Lambertによって1980年に開発され、当初はコモディティ市場の周期的な動きを分析するために用いられました。現在では株式、為替など幅広い資産クラスで利用されています。本戦略では、短期のモメンタムを捉えるための高速CCI(25)と、市場全体の強弱を測定するための低速CCI(50)の2本を同時に使用します。 CCIの値は通常−100から+100の範囲に収まりますが、これを超える動きは極端な強弱を示します。±100のレベルもフィルタとして有効ですが、本戦略ではゼロラインを主要なトリガーとします。CCIがマイナス圏からプラス圏にクロスした場合は上昇モメンタムへの転換、逆にプラス圏からマイナス圏へのクロスは下落圧力の出現を示唆します。

ゼロラインのクロスは±100のブレイクよりも早く発生するため、EAはより迅速にエントリーできますが、ダマシを伴う場合もあります。これを抑制するために、EAでは追加の確認条件を設けています。CCIが+100を上抜けた場合は買いシグナルを強化し、−100を下抜けた場合は売りシグナルを強化します。つまり、ゼロラインクロスが主要なエントリー条件であり、±100のレベルは補助的なフィルタとして機能します。

図2:CCIインジケーター

以下では、MQL5で2つのCCIハンドルを作成し、正しく管理し、取得する方法を説明します。 

  • OnInit()で、以下のようにCCIハンドルを作成します。

handleCCI_Long  = iCCI(_Symbol, _Period, CCI_LongPeriod, PRICE_TYPICAL);
handleCCI_Short = iCCI(_Symbol, _Period, CCI_ShortPeriod, PRICE_TYPICAL);

  • OnTick()でCopyBuffer()を使用してバッファ値をコピーします。

CopyBuffer(handleCCI_Long,  0, 0, 2, cciL);
CopyBuffer(handleCCI_Short, 0, 0, 1, cciS);

34期間のEMA(指数平滑移動平均)

EMAは、直近の価格データにより大きな重みを与え、トレンドをより迅速に反映するインジケーターです。この指数加重により短期的なノイズを除去し、優勢なトレンドを明確に把握できます。1時間足では34期間EMAがよく使用され、これはおおよそ1週間分の取引データをカバーします。  取引は34期間EMAの方向にのみおこないます。価格がEMAの上にある場合は買い方向、下にある場合は売り方向とします。このルールは一貫したトレンドフィルタとして機能し、逆張り取引を避けることができます。

図3:34期間EMA

以下はMQL5で34期間EMAハンドルを作成する方法です。

  • 宣言

input int EMAPeriod = 34;

  • 初期化

handleEMA = iMA(_Symbol, _Period, EMAPeriod, 0, MODE_EMA, PRICE_CLOSE);

  • バッファのコピー(同じロジック):

double ema34[1];
if(CopyBuffer(handleEMA, 0, 0, 1, ema34) < 1) return;
double ema_now = ema34[0];

ATR (Average True Range)

ATRは、J. Welles Wilder Jr.が1978年に著書「New Concepts in Technical Trading Systems」で発表したボラティリティ指標です。通常14期間の平均を用い、市場の実際の価格変動の幅を表します。

本記事では、ATRを用いてストップロス(SL)およびテイクプロフィット(TP)のレベルを算出、設定します。以下はMQL5でこれを実装する方法です。

  • 宣言

int handleATR = INVALID_HANDLE;
input int ATR_Period = 14;

  • 初期化

handleATR = iATR(_Symbol, _Period, ATR_Period);
if(handleATR == INVALID_HANDLE) return INIT_FAILED;

  • バッファのコピー

double atrBuf[1];
if(CopyBuffer(handleATR, 0, 0, 1, atrBuf) > 0)
{
   double atrValue = atrBuf[0];
   // use atrValue here…
}

  • クリーンアップ

if(handleATR != INVALID_HANDLE)
    IndicatorRelease(handleATR);


戦略ロジック

このセクションでは、2本のCCI(商品チャネル指数)と34期間のEMA(指数平滑移動平均)を組み合わせることで、CCIゼロラインEAがどのようにエントリーシグナルを生成するかを説明します。 CCIがゼロラインを上抜けると、市場のモメンタムが上方向へ転換しつつあることを示し、買い圧力の増加や上昇シグナルの発生を示唆します。反対に、CCIがゼロラインを下抜ける場合、市場のモメンタムが下向きに転じ、売り圧力の増加や下降トレンドの発生を示唆します。 

価格が34期間EMAの上に位置する場合、一般的に市場モメンタムが上向きに転換していることを示し、上昇トレンドまたは買いの機会を示唆します。一方で、価格が34期間EMAを下回る場合は、モメンタムの低下や下降トレンドの開始を示唆し、売りの可能性を示します。

使用インジケーター

  • CCI (25):短期CCI
  • CCI (50):長期CCI
  • EMA (34):終値を基準とした34期間の指数平滑移動平均

買いシグナル基準

  • CCI (25)がゼロより上:CCI (25)がゼロラインより上に位置し、短期的な上昇モメンタムを示していること。
  • CCI (50)のゼロライン上抜け:CCI (50)がゼロラインを上抜け、長期的なトレンド転換を確認できること。
  • 価格確認:現在のローソク足がEMA(34)より上で確定し、上昇バイアスが確認できること。

図4:買いシグナル条件

これら3つの条件が新しいバーで同時に満たされた場合、EAは買いシグナルを登録します。

売りシグナル基準

  • CCI (25)がゼロより下:CCI (25)がゼロラインより下に位置し、短期的な下落モメンタムを示していること。
  • CCI (50)のゼロライン下抜け:CCI (50)がゼロラインを下抜け、長期的な下降転換を確認できること。
  • 価格確認:現在のローソク足がEMA (34)より下で確定し、下降バイアスが確認できること。

図5:売りシグナル条件

これら3つの条件が新しいバーで同時に満たされた場合、EAは売りシグナルを登録します。


EAの設計

EAが起動すると、まずチャートからリアルタイムデータを取得するために必要なインジケーターハンドルを設定します。この処理はOnInit()関数内でおこなわれます。ハンドルには、広範なトレンドを特定するための長期CCI(50)と、エントリー確認用の短期CCI(25)、そしてトレンドフィルタとして機能する34期間のEMAが含まれます。具体的には、iCCI()やiMA()などの関数を使用してMetaTrader内部エンジンからこれらのインジケーターを呼び出します。

これらのハンドルを正しく生成することは非常に重要です。いずれかのハンドルが初期化に失敗した場合(INVALID_HANDLEが返される場合)、EAは動作を停止し、誤シグナルや実行時エラーを防ぎます。さらに、ATRを用いたストップロスおよびテイクプロフィット管理が有効(UseATRパラメータ)になっている場合のみ、ATRハンドルが追加で作成されます。

handleCCI_Long  = iCCI(_Symbol, _Period, CCI_LongPeriod, PRICE_TYPICAL);
handleCCI_Short = iCCI(_Symbol, _Period, CCI_ShortPeriod, PRICE_TYPICAL);
handleEMA       = iMA(_Symbol, _Period, EMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
if(UseATR)
    handleATR = iATR(_Symbol, _Period, ATR_Period);

  • このアプローチにより、EAは各ティックで取引判断に必要なすべてのデータを確実に取得できるようになります。

EAには、特定の時間帯に取引を制限する機能が搭載されています。特にUSDJPYでは東京時間帯に限定することで、流動性が低く価格変動が不安定になりやすい時間帯での取引を避けることができます。この機能はOnTick()関数内でサーバー時刻を確認することで実装されます。セッションフィルタが有効な場合、現在時刻をMqlDateTime構造体に変換し、その時間部分をユーザー設定の開始時刻と終了時刻と比較します。現在時刻がこの範囲外であれば、EAは単に処理を終了し、分析や取引をおこないません。

if(UseTokyoSessionFilter && _Symbol == "USDJPY")
{
    datetime now = TimeTradeServer();
    MqlDateTime tm;
    TimeToStruct(now, tm);
    int hr = tm.hour;
    if(hr < TokyoStartHour || hr >= TokyoEndHour)
        return;
}

  • 指定時間内のみ取引をおこなうことで、閑散時間帯に発生しやすいスリッページや誤シグナルのリスクを低減します。

同一のローソク足内で複数回の評価が行われないよう、EAはシンプルながら効果的なロジックを採用しています。最後に処理したバーのタイムスタンプを記録し、新しいバーが形成された場合のみ処理を進めます。これは関数呼び出し間で値を保持するstatic変数lastTimeによって実現されます。新しいティックが到着するたびに、現在のバー時刻(iTime())と記録された値を比較し、一致する場合は処理を終了します。これにより、各バーにつき1回のみ計算と判断が行われます。

static datetime lastTime=0;
datetime t = iTime(_Symbol, _Period, 0);
if(t == lastTime) return; // same bar, skip
lastTime = t;

  • この仕組みにより、パフォーマンスの最適化、シグナルの重複防止、トレーダーの時間軸分析との整合性が保たれ、戦略全体の効率と精度が向上します。

新しいバーが形成されるたびに、EAはCopyBuffer()を呼び出し、最新のCCI (25)、CCI (50)、およびEMA (34)の値を取得します(クロス判定のため前バーも含みます)。長期CCI (50)は広範なトレンドを定義し、短期CCI (25)はエントリーモメンタムを確認します。EMAはノイズを除去し、価格がトレンド方向に沿って動いていることを確認します。ATRベースのストップが有効な場合、EAは同時に現在のATR (14)も取得します。

double cciL[2], cciS[1], emaVal[1];
if(CopyBuffer(handleCCI_Long, 0, 0, 2, cciL) < 2) return;
if(CopyBuffer(handleCCI_Short, 0, 0, 1, cciS) < 1) return;
if(CopyBuffer(handleEMA, 0, 0, 1, emaVal) < 1) return;

double atrValue = 0.0;
if(UseATR)
{
    double atrBuf[1];
    if(CopyBuffer(handleATR, 0, 0, 1, atrBuf) < 1) return;
    atrValue = atrBuf[0];
}

  • これらの取得データがすべての取引判断の基礎となります。CCIは反転や継続の局面を捉え、EMAはトレンド方向との整合性を確認し、ATRはリスクを動的に調整します。

取引ロジックの核心は、長期CCIがゼロラインをクロスするタイミングを検出することにあります。下から上へのクロスは上昇トレンドへの転換を、上から下へのクロスは下降モメンタムの発生を示します。コードでは、長期CCIバッファの前回値と現在値を比較することでこの瞬間を捉えます。クロスが検出されると、EAは追加フィルタを適用します。短期CCIが同方向にあること(上昇時は正、下降時は負)、そして現在の価格がEMAの上または下にあることが条件です。

double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);

if(crossUp && cciS[0] > 0 && price > emaVal[0])
    RegisterSignal(true, price, atrValue);
if(crossDown && cciS[0] < 0 && price < emaVal[0])
    RegisterSignal(false, price, atrValue);

  • この多層的な確認により、誤シグナルを排除し、複数のインジケーターが一致しトレンドフィルターが支持する場合にのみエントリーがおこなわれます。

有効なシグナルが確認されると、EAはストップロス(SL)およびテイクプロフィット(TP)の適切な水準を算出します。ATRベースの管理が有効な場合、これらのレベルは現在のATR値にユーザー指定の倍率(例:1.5)を掛けることで動的に決定されます。SLはエントリー価格から一定距離(ロングの場合は下、ショートの場合は上)に設定され、TPはリスクリワード比に基づいてより遠くに設定されます。

double dist = UseATR ? atrValue * ATR_Multiplier : SLBufferPoints * _Point;
double slPrice = isBuy ? price - dist : price + dist;
double tpPrice = isBuy ? price + dist * RiskRewardRatio : price - dist * RiskRewardRatio;

  • EAは次に、チャート上に視覚的なインジケーションを作成します。エントリーポイントを示す矢印を現在のローソク足付近に表示し、SLおよびTPの水準を示す水平線を描画します。

ObjectCreate(0, name, OBJ_ARROW, 0, barTime, arrowPrice);
ObjectSetInteger(0, name, OBJPROP_ARROWCODE, arrowCode);
ObjectSetInteger(0, name, OBJPROP_COLOR, isBuy ? BuyArrowColor : SellArrowColor);
ObjectCreate(0, "SL_Line", OBJ_HLINE, 0, 0, slPrice);
ObjectCreate(0, "TP_Line", OBJ_HLINE, 0, 0, tpPrice);

  • これらの視覚的表示により、シグナルを目視で確認し、EAのロジックが実際の相場状況と一致していることを確認できます。

視覚マーカーの描画後、EAは取引の詳細(エントリー価格、SL、TP水準)を含むアラートメッセージを発します。

Alert("CCI ZeroLine EMA + R:R 1:1.5 " + (isBuy ? "BUY" : "SELL") +
      StringFormat(" @%.5f | SL: %.5f | TP: %.5f", price, slPrice, tpPrice));

さらに、EAは内部追跡配列を更新します。この配列には、各取引のSLおよびTP水準に加え、取引結果が確定したかどうかを示すブール値が格納されます。これらの配列は、バックテストや実運用中に複数ポジションを同時に管理し、そのパフォーマンスを評価するうえで不可欠です。

signalSL[totalSignals] = slPrice;
signalTP[totalSignals] = tpPrice;
resolved[totalSignals] = false;

ResolveSignals()関数は、アクティブな取引を追跡する役割を担います。すべてのオープンシグナルをループし、直近のバーの高値および安値を確認してSLまたはTPが達成されたかを判定します。高値がTPを超えた場合は勝ち、安値がSLを下回った場合は負けとして記録されます。この処理により、シグナル数と勝ち数のカウンターが更新され、戦略の有効性に関する統計的フィードバックが得られます。この監視はバックテストにおいて特に重要であり、勝率、プロフィットファクター、ドローダウンなどのパフォーマンス指標に直接影響します。

double high = iHigh(_Symbol, _Period, idx);
double low = iLow(_Symbol, _Period, idx);
if(high >= signalTP[i]) { winSignals++; resolved[i]=true; }
if(low <= signalSL[i]) { resolved[i]=true; }

EAが削除されるかチャートが閉じられる際には、OnDeinit()関数がすべてのインジケーターハンドルをIndicatorRelease()で解放します。また、実行中に作成された矢印や水平線などのグラフィックオブジェクトも削除します。適切なリソース管理によりメモリリークを防止し、チャートを整理した状態に保つことで、後続のEAや再実行時にも残留データによる不具合が発生しないようにします。

IndicatorRelease(handleCCI_Long);
IndicatorRelease(handleCCI_Short);
ObjectDelete(0, "SL_Line");
ObjectDelete(0, "TP_Line");

完全なコード

//+------------------------------------------------------------------+
//|                                                  CCI Zero-Line EA|
//|                                   Copyright 2025, MetaQuotes Ltd.|
//|                           https://www.mql5.com/ja/users/lynnchris|
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/ja/users/lynnchris"
#property version   "1.0"
#property strict

#include <Trade\Trade.mqh>
#include <Tools\Datetime.mqh>

CTrade trade;

//--- Session filter for USDJPY (Tokyo session)
input bool UseTokyoSessionFilter = false;  // enable Tokyo session filter
input int  TokyoStartHour        = 0;      // session start hour (server time)
input int  TokyoEndHour          = 9;      // session end hour   (server time, exclusive)

//--- SL/TP settings
input bool   UseATR           = true;   // true = ATR-based SL/TP, false = fixed points
input int    ATR_Period       = 14;     // ATR look-back
input double ATR_Multiplier   = 1.5;    // SL/TP distance = ATR × this
input double SLBufferPoints   = 10.0;   // fallback SL offset in pips if UseATR=false
input double RiskRewardRatio  = 1.5;    // TP = SL × 1.5 (1:1.5 RR)

//--- Indicator periods
input int CCI_LongPeriod   = 50;  // CCI long period (zero-line cross)
input int CCI_ShortPeriod  = 25;  // CCI short period (confirmation)
input int EMAPeriod        = 34;  // EMA period for trend filter

//--- Arrow colors
input color BuyArrowColor  = clrLime;
input color SellArrowColor = clrRed;

//--- Indicator handles
int handleCCI_Long = INVALID_HANDLE;
int handleCCI_Short= INVALID_HANDLE;
int handleEMA      = INVALID_HANDLE;
int handleATR      = INVALID_HANDLE;

//--- Signal-tracking arrays
int    totalSignals = 0;
int    winSignals   = 0;
int    signalBar[];
double signalSL[];
double signalTP[];
bool   resolved[];

//+------------------------------------------------------------------+
//| Expert initialization                                            |
//+------------------------------------------------------------------+
int OnInit()
  {
   handleCCI_Long  = iCCI(_Symbol, _Period, CCI_LongPeriod, PRICE_TYPICAL);
   handleCCI_Short = iCCI(_Symbol, _Period, CCI_ShortPeriod, PRICE_TYPICAL);
   handleEMA       = iMA(_Symbol, _Period, EMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
   if(handleCCI_Long == INVALID_HANDLE ||
      handleCCI_Short== INVALID_HANDLE ||
      handleEMA      == INVALID_HANDLE)
      return(INIT_FAILED);

   if(UseATR)
     {
      handleATR = iATR(_Symbol, _Period, ATR_Period);
      if(handleATR == INVALID_HANDLE)
         return(INIT_FAILED);
     }

   if(RiskRewardRatio <= 0 ||
      ATR_Multiplier   <= 0 ||
      (SLBufferPoints <= 0 && !UseATR))
      return(INIT_FAILED);

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert deinitialization                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(handleCCI_Long  != INVALID_HANDLE)
      IndicatorRelease(handleCCI_Long);
   if(handleCCI_Short != INVALID_HANDLE)
      IndicatorRelease(handleCCI_Short);
   if(handleEMA       != INVALID_HANDLE)
      IndicatorRelease(handleEMA);
   if(UseATR && handleATR != INVALID_HANDLE)
      IndicatorRelease(handleATR);

   ObjectDelete(0, "SL_Line");
   ObjectDelete(0, "TP_Line");
  }

//+------------------------------------------------------------------+
//| Strategy Tester summary                                          |
//+------------------------------------------------------------------+
double OnTester()
  {
   double winRate = totalSignals > 0
                    ? 100.0 * winSignals / totalSignals
                    : 0.0;
   PrintFormat("=== Backtest Win-Rate ===\nSignals: %d  Wins: %d  Win-Rate: %.2f%%",
               totalSignals, winSignals, winRate);
   return(winRate);
  }

//+------------------------------------------------------------------+
//| Tick handler                                                     |
//+------------------------------------------------------------------+
void OnTick()
  {
// Tokyo session filter for USDJPY
   if(UseTokyoSessionFilter && _Symbol == "USDJPY")
     {
      datetime now = TimeTradeServer();
      MqlDateTime tm;
      TimeToStruct(now, tm);
      int hr = tm.hour;
      if(hr < TokyoStartHour || hr >= TokyoEndHour)
         return;
     }

// Only act on new bar
   static datetime lastTime = 0;
   datetime t = iTime(_Symbol, _Period, 0);
   if(t == lastTime)
      return;
   lastTime = t;

// Copy indicator buffers
   double cciL[2], cciS[1], emaVal[1];
   if(CopyBuffer(handleCCI_Long,  0, 0, 2, cciL)  < 2)
      return;
   if(CopyBuffer(handleCCI_Short, 0, 0, 1, cciS)  < 1)
      return;
   if(CopyBuffer(handleEMA,       0, 0, 1, emaVal) < 1)
      return;

// ATR if needed
   double atrValue = 0.0;
   if(UseATR)
     {
      double atrBuf[1];
      if(CopyBuffer(handleATR, 0, 0, 1, atrBuf) < 1)
         return;
      atrValue = atrBuf[0];
     }

   double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);

// Detect zero-line cross
   bool crossUp   = (cciL[1] < 0 && cciL[0] > 0);
   bool crossDown = (cciL[1] > 0 && cciL[0] < 0);

// Confirm with short CCI & EMA trend filter
   if(crossUp   && cciS[0] >  0 && price > emaVal[0])
      RegisterSignal(true,  price, atrValue);
   if(crossDown && cciS[0] <  0 && price < emaVal[0])
      RegisterSignal(false, price, atrValue);

// Resolve pending signals for SL/TP hits
   ResolveSignals();
  }

//+------------------------------------------------------------------+
//| Register new buy/sell signal                                     |
//+------------------------------------------------------------------+
void RegisterSignal(bool isBuy, double price, double atrVal)
  {
   double dist    = UseATR
                    ? atrVal * ATR_Multiplier
                    : SLBufferPoints * _Point;
   double slPrice = isBuy ? price - dist : price + dist;
   double tpPrice = isBuy
                    ? price + dist * RiskRewardRatio
                    : price - dist * RiskRewardRatio;

// Arrow placement just outside the candle
   datetime barTime = iTime(_Symbol, _Period, 0);
   double   barHigh = iHigh(_Symbol, _Period, 0);
   double   barLow  = iLow(_Symbol, _Period, 0);
   double   offset  = 5 * _Point;  // e.g. 5-pip offset

   double arrowPrice = isBuy
                       ? barLow  - offset
                       : barHigh + offset;

// Use numeric arrow codes directly
   int arrowCode = isBuy ? 233 : 234;

   string name = (isBuy ? "BUY_" : "SELL_")
                 + TimeToString(TimeTradeServer(), TIME_SECONDS);
   ObjectCreate(0, name, OBJ_ARROW, 0, barTime, arrowPrice);
   ObjectSetInteger(0, name, OBJPROP_ARROWCODE, arrowCode);
   ObjectSetInteger(0, name, OBJPROP_COLOR,     isBuy ? BuyArrowColor : SellArrowColor);
   ObjectSetInteger(0, name, OBJPROP_WIDTH,     2);

// Draw SL/TP lines
   DrawLine("SL_Line", slPrice, clrRed);
   DrawLine("TP_Line", tpPrice, clrLime);

// Alert
   Alert("CCI ZeroLine EMA + R:R 1:1.5 "
         + (isBuy ? "BUY" : "SELL")
         + StringFormat(" @%.5f | SL: %.5f | TP: %.5f",
                        price, slPrice, tpPrice));

// Track for backtest metrics
   totalSignals++;
   ArrayResize(signalBar,  totalSignals);
   ArrayResize(signalSL,   totalSignals);
   ArrayResize(signalTP,   totalSignals);
   ArrayResize(resolved,   totalSignals);

   signalBar[totalSignals-1] = 0;
   signalSL[totalSignals-1]  = slPrice;
   signalTP[totalSignals-1]  = tpPrice;
   resolved[totalSignals-1]  = false;
  }

//+------------------------------------------------------------------+
//| Resolve pending signals (SL/TP checks)                           |
//+------------------------------------------------------------------+
void ResolveSignals()
  {
   int bars = Bars(_Symbol, _Period);
   for(int i = 0; i < totalSignals; i++)
     {
      if(resolved[i])
         continue;
      signalBar[i]++;
      int idx = signalBar[i];
      if(idx >= bars)
         continue;

      double high = iHigh(_Symbol, _Period, idx);
      double low  = iLow(_Symbol, _Period, idx);

      if(high >= signalTP[i])
        {
         winSignals++;
         resolved[i] = true;
        }
      else
         if(low <= signalSL[i])
           {
            resolved[i] = true;
           }
     }
  }

//+------------------------------------------------------------------+
//| Draw a horizontal SL/TP line                                     |
//+------------------------------------------------------------------+
void DrawLine(string name, double price, color clr)
  {
   if(ObjectFind(0, name) >= 0)
      ObjectDelete(0, name);
   ObjectCreate(0, name, OBJ_HLINE, 0, 0, price);
   ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, name, OBJPROP_WIDTH, 2);
   ObjectSetString(0, name, OBJPROP_TEXT, name);
  }
//+------------------------------------------------------------------+


テスト

以下は本EAのバックテスト結果です。

USDJPYでEAをバックテストした結果、以下は操作ログに記録されたログです。

  • 入力
2025.06.30 09:34:54.140   UseTokyoSessionFilter=false
2025.06.30 09:34:54.140   TokyoStartHour=0
2025.06.30 09:34:54.140   TokyoEndHour=9
2025.06.30 09:34:54.140   UseATR=true
2025.06.30 09:34:54.140   ATR_Period=14
2025.06.30 09:34:54.140   ATR_Multiplier=1.5
2025.06.30 09:34:54.140   SLBufferPoints=10.0
2025.06.30 09:34:54.140   RiskRewardRatio=1.0
2025.06.30 09:34:54.140   CCI_LongPeriod=50
2025.06.30 09:34:54.140   CCI_ShortPeriod=25
2025.06.30 09:34:54.140   EMAPeriod=34
2025.06.30 09:34:54.140   BuyArrowColor=65280
2025.06.30 09:34:54.140   SellArrowColor=255

設定内容によると、東京時間フィルタは無効化されており、特定の東京市場時間外でも取引が行われる状態になっています。ただし、このフィルタを有効にした場合は、東京時間の00:00から09:00の間に取引が制限されます。EAは期間14のATRインジケーターを使用し、現在の市場ボラティリティに基づいてストップロスおよびテイクプロフィットの水準を調整するために1.5倍の乗数を適用しています。早期の損切りを防止するためにストップロスには10ポイントのバッファが追加されており、リスクリワード比は1:1に設定され、損益のバランスを保っています。

CCIは、ロングシグナルに50期間、ショートシグナルに25期間を使用しており、戦略には34期間のEMAが組み込まれています。視覚的なシグナルは矢印で表示され、緑が買いシグナル、青が売りシグナルを示しており、それぞれのカラーコードに対応しています。

パラメータ入力は任意にカスタマイズすることが可能です。ここで示した設定は筆者がテスト時に使用したものであり、良好な結果を得られたと判断しています。

  • シグナル

2025.06.30 09:34:54.447 2025.01.02 15:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @157.01800 | SL: 157.45075 | TP: 156.58525
2025.06.30 09:34:56.655 2025.01.09 06:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @158.04800 | SL: 158.30343 | TP: 157.79257
2025.06.30 09:40:35.572 2025.01.29 07:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @155.21100 | SL: 155.47704 | TP: 154.94496
2025.06.30 09:40:36.759 2025.01.31 07:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 BUY @154.68200 | SL: 154.25439 | TP: 155.10961
2025.06.30 09:40:45.870 2025.02.26 06:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 BUY @149.42600 | SL: 149.05400 | TP: 149.79800
2025.06.30 09:40:46.737 2025.02.28 03:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @149.51900 | SL: 150.03650 | TP: 149.00150
2025.06.30 09:40:49.726 2025.03.06 01:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @149.14200 | SL: 149.68200 | TP: 148.60200
2025.06.30 09:40:58.522 2025.03.21 16:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @148.97200 | SL: 149.35418 | TP: 148.58982
2025.06.30 09:41:01.363 2025.04.01 00:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 BUY @149.93500 | SL: 149.57425 | TP: 150.29575
2025.06.30 09:41:16.557 2025.04.28 13:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @143.33500 | SL: 143.69232 | TP: 142.97768
2025.06.30 09:41:36.021 2025.06.18 14:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @144.85500 | SL: 145.08911 | TP: 144.62089
2025.06.30 09:41:37.454 2025.06.23 19:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @146.15000 | SL: 146.65711 | TP: 145.64289
2025.06.30 09:41:38.360 2025.06.25 23:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @145.21700 | SL: 145.47286 | TP: 144.96114

  • 勝率

2025.06.30 09:41:39.206 2025.06.29 23:59:59   Signals: 13  Wins: 10  Win-Rate: 76.92%

以下のGIFはテスト結果を可視化したものです。赤い下向き矢印は売りシグナル、緑の上向き矢印は買いシグナルを示しています。赤いラインはストップロス水準を、緑のラインはテイクプロフィットの目標値を示しています。


結論

まとめとして、このCCIゼロラインEAは明確でルールベースのエントリーに必要な要素をすべてチャート上に表示します。

  • 長期CCI(50)で大きなトレンドを把握し、短期CCI(25)でモメンタムの方向を確認することで、両者が一致したときにのみエントリーをおこないます。
  • 34期間EMAにより、一時的なスパイクに追随することを防ぎます。価格がEMAの正しい側で確定した場合、そのトレンドが正当であると判断できます。
  • ATRベースのストップにより、ストップロスの距離は市場のボラティリティに応じて変動します。穏やかな相場ではタイトに、活発な相場では広く設定されます。利益目標はそれに比例して設定され、安定したリスクリワード比を維持します。

この組み合わせにより、価格、モメンタム、ボラティリティがすべて同じ方向を示すときにのみ矢印が表示されます。その結果、誤シグナルの発生を抑え、柔軟なリスク管理と各エントリーとエグジット水準の明確な根拠を確保できます。CCIの期間、EMAの長さ、ATRの乗数は、銘柄や時間軸に合わせて調整することができます。堅牢で多層的な分析に基づくツールとして、自信を持って取引をおこなうことが可能です。 本EAは教育目的で提供されるものであり、十分なバックテストやデモ口座での検証を行わずに実際の市場で運用することはお控えください。実運用を検討する前に、結果に十分満足できることを確認してください。





   
ChartProjector
Analytical Comment
Analytics Master
Analytics Forecaster 
Volatility Navigator
Mean Reversion Signal Reaper
Signal Pulse 
Metrics Board 
External Flow
VWAP
Heikin Ashi   FibVWAP  
RSI DIVERGENCE
Parabolic Stop and Reverse (PSAR) 
Quarters Drawerスクリプト
Intrusion Detector
TrendLoom Tool  Quarters Board 
ZigZag Analyzer  Correlation Pathfinder  Market Structure Flip Detector Tool
Correlation Dashboard   Currency Strength Meter 
PAQ Analysis Tool 
Dual EMA Fractal Breaker
Pin bar, Engulfing and RSI divergence
Liquidity Sweep Opening Range Breakout Tool Boom and Crash Interceptor CCI Zer-Line EA

MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/18551

添付されたファイル |
CCI.mq5 (18.28 KB)
MQL5で他の言語の実用的なモジュールを実装する(第1回):Pythonにヒントを得たSQLite3ライブラリの構築 MQL5で他の言語の実用的なモジュールを実装する(第1回):Pythonにヒントを得たSQLite3ライブラリの構築
Pythonのsqlite3モジュールは、SQLiteデータベースを扱うためのシンプルで高速かつ便利な方法を提供しています。本記事では、MQL5に組み込まれているデータベース操作用の関数群を活用し、Pythonのsqlite3モジュールと同様の操作感でSQLite3データベースを扱える独自モジュールを構築します。
知っておくべきMQL5ウィザードのテクニック(第72回):教師あり学習でMACDとOBVのパターンを活用する 知っておくべきMQL5ウィザードのテクニック(第72回):教師あり学習でMACDとOBVのパターンを活用する
前回の記事で紹介したMACDとOBVのインジケーターペアをフォローアップし、今回はこのペアを機械学習でどのように強化できるかを見ていきます。MACDとOBVは、それぞれトレンド系と出来高系という補完的なペアです。私たちの機械学習アプローチでは、畳み込みニューラルネットワーク(CNN)を使い、カーネルとチャンネルのサイズを調整する際に指数カーネルを利用して、このインジケーターペアの予測をファインチューニングします。今回もこれまでと同様に、MQL5ウィザードでエキスパートアドバイザー(EA)を組み立てられるようにしたカスタムシグナルクラスファイル内で実装しています。
共和分株式による統計的裁定取引(第1回):エングル=グレンジャーおよびジョハンセンの共和分検定 共和分株式による統計的裁定取引(第1回):エングル=グレンジャーおよびジョハンセンの共和分検定
本記事は、トレーダー向けに、最も一般的な共和分検定を優しく紹介し、その結果の理解方法を簡単に解説することを目的としています。エングル=グレンジャーおよびジョハンセンの共和分検定は、長期的なダイナミクスを共有する統計的に有意な資産のペアやグループを特定するのに有効です。特にジョハンセン検定は、3つ以上の資産を含むポートフォリオに対して有用で、複数の共和分ベクトルの強さを一度に評価できます。
MQL5での取引戦略の自動化(第21回):適応学習率によるニューラルネットワーク取引の強化 MQL5での取引戦略の自動化(第21回):適応学習率によるニューラルネットワーク取引の強化
本記事では、MQL5におけるニューラルネットワーク取引戦略を、適応型学習率を用いて精度を向上させる形で強化します。このメカニズムを設計・実装した後、そのパフォーマンスを検証します。記事の最後には、アルゴリズム取引における最適化の知見もまとめます。